如何在节点 js 中等待子查询结果?
Posted
技术标签:
【中文标题】如何在节点 js 中等待子查询结果?【英文标题】:How to wait for subquery results in node js? 【发布时间】:2020-03-07 19:07:15 【问题描述】:我将 Graphql 与 MongoDB 一起使用。在解析器中使用了子查询,但在执行子查询时主查询返回数据不等到子查询完成。 我想在解析器中使用子查询参数来操作主查询。
return await Articles.find( Status: 1, isPublish : true )
.sort(TotalClapCount:-1)
.sort(ViewCount:-1)
.skip( offset )
.limit(limit)
.then( async ( lor ) => await
lor.forEach(async function(data, key)
data["isBookmark"] =
await ArticleBookmarks
.find( ArticleID : data["ID"], UserID : ArgsUserID, Status : 1 )
.countDocuments()
.then( (hre) =>return (hre == 1) ? true : false; );
);
);
return lor;
);
我想在单个查询中显示带有书签的文章列表,但 return lor 在子查询操作之前执行。 async-await 如何为此工作?
【问题讨论】:
如果您使用适当的缩进格式化您的代码,我们将有更好的机会遵循它正在尝试做的事情。 因此,.forEach()
不会等待返回的承诺,因此其回调中的 await
不会做任何有用的事情。更改为常规的for
循环,您将有更好的机会。如果不将代码复制到编辑器中并正确格式化,我真的不知道它还试图做什么。
@RatanUdayKumar - 您的解决方案有多个问题。它不会起作用。
@jfriend00 您可以发布您的解决方案。并希望对调查问卷有所帮助。
请检查我的解决方案。希望它会工作
【参考方案1】:
尝试如下
let MainFunction = () =>
return new Promise(async (resolve, reject) =>
try
let async = require("async");
let query =
Status: 1,
isPublish: true
let sortOptions =
TotalClapCount: -1,
ViewCount: -1
let Data = await Articles.find(query).sort(sortOptions).skip(offset).limit(limit).lean();
async.eachSeries(Data, async (data, callback) =>
try
let cquery =
ArticleID: data["ID"],
UserID: ArgsUserID, Status: 1
;
let countedArticles = await ArticleBookmarks.countDocuments(cquery).lean();
data.isBookmark = (countedArticles >= 1) ? true : false;
callback();
catch (error)
callback(error);
, async (err) =>
if (err) reject(err);
resolve(Data);
);
catch (error)
console.error(error);
reject(error);
);
return await MainFunction();
注意:请根据需要在 MainFunction 中传递必要的参数。
【讨论】:
你不应该围绕现有的承诺包装一个新的承诺。这是一个承诺反模式。正如我建议 OP,只需将.forEach()
切换到常规 for
循环,然后 await
将停止循环。加上await Data.forEach()
没有任何用处,因为.forEach()
没有返回值。而.forEach()
循环内的await
不会导致循环停止。此代码不能解决任何问题。
我已经更新了我的解决方案。它一定会奏效的。
哇。请注意 OP,这确实不是使用 Promise 进行编程的最佳方式。当我使用可以编写代码的计算机时,我将向您展示一种更好的方法。正如我之前所说,将现有的 Promise 包装在一个新的手动构造的 Promise 中被认为是一种反模式。而且,通过正确使用for
循环和async/await
,完全不需要async.series
库函数。
使用异步库,问卷可能会从异步库中受益,对他未来的复杂功能很有用,问卷可以顺利使用。
@RatanUdayKumar 谢谢,上面的解决方案工作正常。【参考方案2】:
您可以像这样使用常规的 Promise、常规的 for
循环和 async/await
。这里不需要async.series()
这样的外部库函数:
let lor = await Articles.find( Status: 1, isPublish: true)
.sort(TotalClapCount: -1)
.sort(ViewCount: -1)
.skip(offset)
.limit(limit);
for (let data of lor)
let hre = await ArticleBookmarks.find(
ArticleID: data.ID,
UserID: ArgsUserID,
Status: 1
).countDocuments();
data.isBookmark = (hre == 1);
return lor;
变化:
return await Articles.find()
没有任何用处,因为它与 return Articles.find()
没有什么不同,因为两者都只返回一个承诺,因为它位于 async
函数内,并且所有 async
函数都返回一个承诺。
将.forEach()
更改为常规for
循环,因为for
循环将暂停await
函数的执行。 forEach()
不会暂停循环。
await xxx.forEach()
没有 await
任何东西,因为 .forEach()
没有返回值,而 await
只有在你 await
一个承诺时才会做一些有用的事情。
将(hre == 1) ? true : false;
更改为(hre == 1)
,因为hre == 1
已经是一个布尔值,所以不需要? true : false
。就个人而言,如果您知道数据实际上是数字而不是字符串,我可能会使用 (hre === 1)
,因为 ===
几乎总是比 ==
更可取(没有隐式或意外的类型转换)。
其他cmets:
await
只应在您等待承诺时使用。那是唯一一次做有用的事情。
将new Promise()
包裹在其他promise 返回函数周围被认为是promise anti-pattern,因为它不是必需的,并且会为编程错误创造许多机会,尤其是在错误处理方面。您可以直接返回并使用您已经拥有的承诺,而无需包装新的承诺。
async.eachSeries()
没有提供任何使用 async/await
进行常规 Promise 排序的功能,除非您尝试在没有 async/await
的环境中运行。
当您希望 await
暂停循环时,请使用常规的 for
循环。它不会暂停 .map()
或 .forEach()
等数组方法的循环,因为这些迭代方法不支持承诺。他们不会在回调返回的承诺上暂停(当您将回调声明为 async
时会发生这种情况)。
并行查询的另一种可能性
由于循环的一个迭代的处理独立于其他迭代,您可以并行运行所有这些数据库查询并使用Promise.all()
知道它们何时完成。这有一个缺点,如果数组很大,您将一次向数据库抛出大量查询。好处是,如果数据库没有被同时查询的数量压得喘不过气来,端到端的处理时间可能会更短。你可以这样做:
let lor = await Articles.find( Status: 1, isPublish: true)
.sort(TotalClapCount: -1)
.sort(ViewCount: -1)
.skip(offset)
.limit(limit);
await Promise.all(lor.map(async data =>
let hre = await ArticleBookmarks.find(
ArticleID: data.ID,
UserID: ArgsUserID,
Status: 1
).countDocuments();
data.isBookmark = (hre == 1);
);
return lor;
【讨论】:
我仍然认为混合.then
和await
并不是最好的。应该更简单。
@Ashh - 是的,我同意。我简化了我的答案。【参考方案3】:
forEach()
不会等待异步操作完成。您可以使用内部带有await
的for 循环。但是,缺点是其他运营商必须等待前一个运营商完成。适当的解决方案是使用Promise.all
,它返回一个单一的 Promise,当所有作为迭代传递的 Promise 都已解决时,该 Promise 将解决。希望这会有所帮助。
【讨论】:
如果 OP 想要对他们的循环进行排序(这是他们似乎想要做的,并且可能有助于避免过多并行查询压倒数据库),那么就不需要 @987654325 @。请参阅我的答案以了解如何完成。 在发布我的答案之前,我确实阅读了您的答案。我在作者的代码中没有看到任何理由来对他的循环进行排序。 “......可能有助于不要用太多并行查询压倒数据库”:我同意你的观点。但是,作者可能还希望进行并行查询以减少响应时间。好消息是我们提出了两种解决方案,哈哈! 你的分数很好。我在答案中添加了并行版本的代码,并解释了与排序相比的相对优缺点。以上是关于如何在节点 js 中等待子查询结果?的主要内容,如果未能解决你的问题,请参考以下文章