Node.js异步场景下数组push后打印为空的问题求助
Node.js OrderPlace接口meta数组为空问题解决办法
问题描述
编写Node.js的OrderPlace接口时,从请求体获取product_info数组并初始化空数组meta;遍历product_info,通过Ticket.find查询数据库,满足条件时向meta中push包含cart_id、pro_id、quantity的对象,但最终console.log(meta)输出为空,无法得到预期的数组数据。
原代码:
exports.OrderPlace = (req, res) => { const all_product = req.body.product_info; let meta = []; all_product.forEach((element) => { Ticket.find({ _id: element.product_id }, function (err, docs) { if (err) return handleError(err); if (docs[0]._id == element.product_id) { if (element.quantity < docs[0].quantity) { meta.push({ cart_id: element.id, pro_id: element.product_id, quantity: element.quantity + " Asking quentity is not available!", }); } } }); }); console.log(meta); };
问题原因
- 异步执行顺序问题:
Ticket.find是异步数据库操作,forEach会快速遍历完所有元素,但此时所有数据库查询还未完成,console.log(meta)就已经执行,所以meta还是空数组。 - 冗余判断:查询条件是
_id: element.product_id,如果查询到数据,docs[0]._id必然和element.product_id匹配,无需额外判断。 - 拼写错误:
quentity应为quantity。
解决方案
方案一:用async/await + for...of(串行处理)
将接口函数声明为async,使用for...of遍历数组(支持异步等待),确保每个数据库查询完成后再处理下一个:
exports.OrderPlace = async (req, res) => { const all_product = req.body.product_info; let meta = []; for (const element of all_product) { try { // 用findOne代替find,按_id查询仅返回单个文档 const doc = await Ticket.findOne({ _id: element.product_id }); if (doc) { if (element.quantity < doc.quantity) { meta.push({ cart_id: element.id, pro_id: element.product_id, quantity: `${element.quantity} Asking quantity is not available!` }); } } } catch (err) { handleError(err); // 可选:出现错误时直接返回响应 // return res.status(500).json({ error: err.message }); } } console.log(meta); // 记得返回接口响应 res.status(200).json(meta); };
方案二:用Promise.all(并行处理,效率更高)
将所有数据库查询包装成Promise数组,用Promise.all并行执行,等待所有查询完成后再处理结果:
exports.OrderPlace = async (req, res) => { const all_product = req.body.product_info; // 生成所有查询的Promise const queryPromises = all_product.map(async (element) => { try { const doc = await Ticket.findOne({ _id: element.product_id }); // 满足条件时返回对象,否则返回null if (doc && element.quantity < doc.quantity) { return { cart_id: element.id, pro_id: element.product_id, quantity: `${element.quantity} Asking quantity is not available!` }; } return null; } catch (err) { handleError(err); return null; } }); // 等待所有Promise完成,过滤掉null值得到最终meta const meta = (await Promise.all(queryPromises)).filter(item => item !== null); console.log(meta); res.status(200).json(meta); };
额外说明
- 使用
findOne替代find:按_id查询MongoDB只会返回单个文档,findOne直接返回文档对象,无需取docs[0],更简洁高效。 - 完善错误处理:异步操作必须捕获错误,避免未处理的Promise异常导致程序崩溃。
- 补充响应返回:原代码缺少接口响应,需添加
res.status(...).json(...)语句,否则客户端会一直处于等待状态。
内容的提问来源于stack exchange,提问作者K2 G
相关产品推荐
相关产品推荐

