如何在forEach中同步执行Mongoose查询,保证顺序执行?
解决异步查询在forEach中执行顺序的问题
嘿,这个问题我太熟了——本质是同步循环和异步操作的执行顺序冲突:forEach是同步遍历,会一口气把所有console.log("x")跑完,而CustomerAgreement.findOne()是异步的Promise操作,它会被放到事件队列里排队,等所有同步代码(包括forEach和最后的console.log("z"))都执行完,才会挨个执行这些异步回调,所以就出现了先打所有x、再打z、最后打所有y的情况。
下面给你几个能实现x y x y x y x y x y z期望输出的方案,按需选就行:
方案一:用for...of配合await(最直观好懂)
把forEach换成支持异步的for...of循环,这样每次遍历都会等当前的异步查询完成后,再进入下一轮:
Case.find({}, {case_id:1}).then(async recs => { for (const rec of recs) { console.log("x"); const t = await CustomerAgreement.findOne({case_id: rec.case_id}); console.log("y"); } console.log("z"); });
这种方式会严格按顺序走:处理第一条Case→打x→等查询完→打y→再处理第二条,以此类推,最后打z,完全符合你的期望。
方案二:用Promise链控制顺序(兼顾一点并行性)
如果你想让所有CustomerAgreement查询尽量并行发起,但依然按Case的顺序输出x和y,可以用reduce把每个异步操作串成Promise链:
Case.find({}, {case_id:1}).then(recs => { return recs.reduce((prevPromise, rec) => { return prevPromise.then(() => { console.log("x"); return CustomerAgreement.findOne({case_id: rec.case_id}).then(t => { console.log("y"); }); }); }, Promise.resolve()); }).then(() => { console.log("z"); });
这里通过reduce把每个步骤连起来,前一个操作完成后才会启动下一个,既保证了输出顺序,又比纯串行的for...of灵活一点。
方案三:直接用MongoDB的关联查询(效率最高)
如果你的Case集合里的case_id是引用CustomerAgreement的外键,完全不用手动循环查询——用Mongoose的populate方法就能一次性把关联数据拉回来:
// 前提是Case模型里已经把case_id定义为对CustomerAgreement的引用 Case.find({}, {case_id:1}).populate('case_id').then(recs => { recs.forEach(rec => { console.log("x"); console.log("y"); // 这里rec.case_id就是对应的CustomerAgreement文档 }); console.log("z"); });
这种方式只需要一次数据库查询,比循环查5次效率高多了,推荐优先考虑这种。
内容的提问来源于stack exchange,提问作者Kalviz
相关产品推荐
相关产品推荐

