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

Javascript 当从异步函数返回查询对象时,如何防止Knex.js运行该对象?,javascript,node.js,knex.js,Javascript,Node.js,Knex.js,我有一个node.js后端,它使用Knex.js从各种输入动态构造DB查询。一些输入需要异步处理。我的问题是,我不能从异步函数(当然也不能在承诺解析函数中)返回knex查询对象,因为这会触发查询的执行。目前,我需要先处理所有异步输入,然后再将它们交给查询构建函数,但这确实限制了它们的可组合性。有没有办法防止Knex在异步上下文中执行查询对象?您需要将生成器包装到函数或对象: 异步函数returnsQueryBuilder(){ 返回{builder:knex('mytable')。其中('foo

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

您需要将生成器包装到函数或对象:

异步函数returnsQueryBuilder(){
返回{builder:knex('mytable')。其中('foo','bar')};
}
const query=(wait returnsQueryBuilder()).builder;
因为异步函数实际上包装和解析返回值/promises/thenables和
knex
查询生成器是
thenable
(第1.2章),所以它会自动重新求解

出于同样的原因,您也可以直接等待查询生成器从生成的查询中获取结果。如果knex查询生成器不为
表格
,则这也将不起作用:

//返回表中的所有行
const result=等待knex(“表”);

因此,正如我所说的,唯一的选择是不直接返回查询生成器实例,而是将其包装为非
表格
多亏了Mikael Lepistö的回答,我知道了如何解决这个问题。正如他所指出的,Knex查询是
thenables
,因为它具有
then
函数。JavaScript wait关键字实际上调用了您呈现给它的任何对象的
then
函数,不管它是否承诺。因此,为了防止在await(或.then())上执行查询,您可以删除/重命名查询
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());
更新,警告:不要在家里尝试此功能孩子:)

我觉得有必要指出米凯尔在评论中的合理批评。这是编写自己的包装器类的一个很有技巧且潜在危险的捷径,可能会使代码更难理解。但我也坚持我的评估,在我的特定用例中正确地键入TypeScript是一个有效的解决方案


更新2:现在不要弄乱原型:)。设置。然后在实例上设置为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("*")

嗨,迈克。谢谢你的回答。包装查询对象肯定会奏效。但这也会使API变得不那么优雅。我想知道是否有可能重命名Knex QueryBuilder上的“then”函数,并添加一个触发“then”并返回承诺的执行函数。如果API使用特定的实践,比如使用具有特定键的对象,那么生成器传递查询生成器的方式就没有调用承诺()那么优雅了为了能够在以后触发该查询。。。不管怎样,如果你想让它更优雅,您可以编写自己的类,将query builder实例封装在其中,并实现一些帮助程序,使其具有比普通对象和魔法属性更好的API。将此答案与Github问题交叉链接:重命名
。然后从query builder中调用
函数是一个非常糟糕的主意,如果您使用其他希望将knex querybuilder Intance传递给他们。此外,没有任何东西可以保证查询生成器在没有then方法的情况下继续正常工作。Monkeypatching/breaking库内部通常是一件坏事,在这种情况下,它完全没有必要,甚至会使使用过库的人更难阅读您的代码。这可能在您的特定用例中有效,但不应作为一种好的方法来推广……而且对整个库进行猴子补丁也是可能的,但更糟糕的是。总的来说,我同意您的评估,特别是关于猴子补丁的评估。不过,更改查询实例的“then”方法似乎是一个合理的风险。另外,我正在将项目从Flow的旧版本(Knex打字非常糟糕)切换到TypeScript,并将正确地键入派生的Knex变体。这将使它在不使用包装器类的情况下节省足够的资源。但总的来说,我同意这是一种针对特定用例的方便而有效的黑客攻击,但不是一种通用的解决方案。这种方法有一个明确的令人恶心的因素:)在不影响原型的情况下,你是如何看待它的,米凯尔没什么大不了的,它仍然是无效的(猴子补丁的)knex查询生成器,可能有效,也可能无效。可能在您的项目中仍然有效(很好:)