从异步函数返回查询对象时,如何防止 Knex.js 来自 运行 查询对象?

How to prevent Knex.js from running a query object when returning it from an async function?

我有一个 node.js 后端,它使用 Knex.js 从各种输入动态构建数据库查询。一些输入需要异步处理。我的问题是,我不能 return 来自异步函数(或者当然是 Promise 解析函数)的 knex 查询对象,因为这会触发查询的执行。目前,我需要先处理所有异步输入,然后再将它们交给查询构建函数,但这确实限制了它们的可组合性。有没有办法阻止 Knex 在异步上下文中执行查询对象?

您需要将构建器包装到函数或对象中:

async function returnsQueryBuilder() {
  return { builder : knex('mytable').where('foo', 'bar') };
}

const query = (await returnsQueryBuilder()).builder;

因为异步函数实际上包装并解析了 returned values/promises/thenables 并且 knex 查询构建器是一个 thenablehttps://promisesaplus.com/ 第 1.2 章)它得到了解析自动。

出于同样的原因,您也可以直接等待查询构建器从构建的查询中获取结果。如果没有 knex 查询生成器 thenable,这也不起作用:

// returns all the rows of the table
const result = await knex('table'); 

所以正如我所说,唯一的选择是不直接 return 查询生成器实例,而是将其包装到不是 thenable.

的东西中

感谢 Mikael Lepistö 的回答,我知道了如何解决这个问题。正如他指出的那样,Knex 查询由于具有 then 功能而 thenables。 JavaScript await 关键字实际上调用了您提供给它的任何 object 的 then 函数,无论是否承诺。因此,为了防止在等待(或 .then())时执行查询,您可以 remove/rename 查询 then 函数。例如

const getQuery = async () => {
  const qb = knex("users")
    .select("id")
    .limit(100);
  qb.promise = qb.then;
  qb.then = undefined;
  return qb;
};

const query = await getQuery();
console.log(query.toString());
console.log(await query.promise());

更新,警告不要在家里尝试这个孩子s :)

我觉得有必要在评论中指出 Mikael 的有效批评。这是编写自己的包装器 class 的 hacky 和潜在危险的捷径,可能会使您的代码更难理解。但我也坚持我的评估,即在我的特定用例中使用正确的 TypeScript 键入它是一个有效且高效的解决方案。

UPDATE2:现在不要弄乱原型 :)。在实例上将 .then 设置为 undefined 就可以了。

对于我的需要,最重要的是提取 where 子句,因为这通常是复杂的部分并且在多个查询之间共享。令人高兴的是,使用 Knex 可以轻松做到这一点。

例如,如果您有此查询,并且 where 函数是与其他查询共享的内容。

const data = await knex("mytable")
   .where(builder => {
      builder.whereNull("mytable.deleted_at")

      if (something) {
         builder.where("something", 42)
      }
   })
   .select("*")

您可以重构为:

const makeWhereClause = ({ something }) => builder => {
   builder.whereNull("mytable.deleted_at")
   if (something) {
      builder.where("something", 42)
   }
}

const data = await knex("mytable")
   .where(makeWhereClause({something})
   .select("*")