Mongoose异步查询结果无法推入数组 返回no items问题
问题描述
使用Mongoose执行查询时可以正常匹配到目标数据,但无法将查询结果推入目标数组,代码运行输出no items,本质是异步函数执行时序问题:日志打印触发时,数据推入操作还未执行完成。
原问题代码如下:
const getOrders = function(allOrders){ let promise = new Promise((succ, fail)=>{ let ordersTodisplay = [] for (let order of allOrders) { if (!(order.orderId === null || order.orderItem === null)){ postMong.findById(order.orderItem, function (err, item) { ordersTodisplay.push(item) }) } } if(ordersTodisplay.length > 0){ succ(ordersTodisplay) } else{ fail("no items") } }) return promise } router.get('/accountpage',function(req,res){ const userDB = req.session.username if (userDB !== undefined && userDB){ userForm.findOne({ username : userDB }, function (err, user) { const userOrders = user.userOrders; if (userOrders.length > 1) { getOrders(userOrders).then((result)=>{console.log(result)}, (fail)=>{console.log(fail)}) res.render('../view/accountpage',{username: userDB,orders: itemsToDisplay}); } else{ res.render('../view/accountpage',{username: userDB,orders: "There are no orders"}); } }); } else { res.redirect("/login") } });
问题原因
postMong.findById是异步回调函数,for循环是同步执行的,循环遍历完成时所有数据库查询的回调还没触发,此时ordersTodisplay是空数组,直接走失败分支返回no items。- 路由逻辑存在额外问题:
getOrders是异步返回Promise的函数,代码没有等待Promise返回结果就提前执行res.render,且引用了未定义的变量itemsToDisplay,就算查询完成也会触发变量未定义错误。
修复代码
const getOrders = async function(allOrders){ // 提前过滤无效订单 const validOrders = allOrders.filter(order => order.orderId !== null && order.orderItem !== null) // 等待所有数据库查询执行完成 const ordersTodisplay = await Promise.all( validOrders.map(order => postMong.findById(order.orderItem).exec()) ) if(ordersTodisplay.length > 0){ return ordersTodisplay } throw new Error("no items") } router.get('/accountpage', function(req,res){ const userDB = req.session.username if (!userDB) { return res.redirect("/login") } userForm.findOne({ username : userDB }, async function (err, user) { if(err) return res.status(500).send("服务异常") const userOrders = user.userOrders; if (userOrders.length > 0) { try { const orderList = await getOrders(userOrders) console.log(orderList) res.render('../view/accountpage',{username: userDB, orders: orderList}); } catch (e) { console.log(e) res.render('../view/accountpage',{username: userDB, orders: "There are no orders"}); } } else { res.render('../view/accountpage',{username: userDB, orders: "There are no orders"}); } }); });
修复要点
- 不要在同步循环中嵌套异步回调后立刻判断结果,用
Promise.all等待所有异步查询全部执行完毕,再做结果校验和返回,彻底解决时序问题。 - Mongoose查询调用
.exec()方法可以返回标准Promise,配合async/await书写异步逻辑更清晰,不需要手动构造Promise包装。 - 路由层必须等待异步查询拿到最终结果后,再执行
res.render返回响应,禁止在异步操作未完成时提前返回响应,同时保证变量名一致,不要引用未定义的变量。 - 原代码判断订单数量的条件
userOrders.length > 1存在逻辑问题,用户仅有1条有效订单时会被判定为无订单,可根据实际业务需求调整判断阈值。
内容的提问来源于stack exchange,提问作者Cristian Babalau
相关产品推荐
相关产品推荐

