Node.js+MongoDB API接口返回JSON数组失败:factitems数组始终为空
问题排查:异步操作导致数组为空的原因及解决办法
嘿,这个问题我太熟了——核心原因是你没处理好MongoDB操作的异步特性!
你看,db.collection('factslist').findOne()是异步函数,它不会阻塞代码执行。你的for循环会飞快地跑完10次迭代,把10个异步查询发出去,但这些查询的回调函数要等数据库返回结果才会触发。而你在循环结束后立刻就打印factitems并发送响应,这时候所有回调都还没执行,数组当然是空的。
给你两个最实用的解决方案,选一个适合你的:
方案1:用async/await + Promise.all(最易读,并行查询)
把路由处理函数改成异步函数,用Promise.all批量处理所有查询,这样能等所有数据库请求都完成后再返回结果:
app.get('/facts', async (req, res) => { try { const idArr = getRandomArray(); // 你的10个字符串id数组 console.log(idArr); // 把每个id的查询包装成Promise,并行执行 const factitems = await Promise.all( idArr.map(id => { // 重点!数据库的_id是int类型,必须把字符串转成数字,否则查不到 const details = { '_id': parseInt(id) }; return db.collection('factslist').findOne(details); }) ); // 过滤掉不存在的id对应的null值(可选) const validItems = factitems.filter(item => item !== null); console.log(JSON.stringify(validItems)); res.send({ factitems: validItems }); } catch (err) { // 统一处理所有错误,避免重复响应 res.send({ error: 'An error has occurred while fetching data' }); } });
方案2:用find+$in一次性查询(更高效,少次数据库请求)
既然你要查多个id,直接用MongoDB的$in操作符一次性查询所有匹配的文档,比循环调用10次findOne效率高多了:
app.get('/facts', (req, res) => { const idArr = getRandomArray(); // 你的10个字符串id数组 console.log(idArr); // 先把字符串id转成数字,匹配数据库的_id类型 const numericIds = idArr.map(id => parseInt(id)); db.collection('factslist').find({ '_id': { $in: numericIds } }).toArray((err, items) => { if (err) { res.send({ error: 'An error has occurred while fetching data' }); } else { console.log(JSON.stringify(items)); res.send({ factitems: items }); } }); });
额外提醒
- 一定要注意
_id的类型匹配!你说数据库里的_id是int类型,但你传的是字符串,这时候findOne会查不到数据,所以必须用parseInt转成数字,这点很容易忽略。 - 原来的错误处理有问题:如果某个
findOne出错,你直接res.send,但其他查询还在进行,会导致Cannot set headers after they are sent to the client的错误,用try/catch或者统一的回调错误处理更稳妥。
内容的提问来源于stack exchange,提问作者MostlyAff
相关产品推荐
相关产品推荐

