NodeJS中MongoJS查询结果存入数组并修改失败求助
解决MongoJS异步操作导致返回数据未修改的问题
你完全猜对了!问题的核心就是异步操作的执行顺序搞鬼了。MongoDB的所有查询都是异步的——db.faculties.find()不会立刻返回结果,它会先把查询任务丢给数据库,然后继续往下执行你的代码,等数据库返回结果了才会跑回调函数里的逻辑。
你的代码里,res.send(data)是在forEach循环结束后立刻执行的,这时候所有db.faculties.find()的回调都还没机会执行,subject.faculty根本没被赋值,自然返回的是原始的data数组。
下面给你两种清晰的解决方案,优先推荐第一种现代写法:
方案一:用async/await + Promise(最易读)
首先我们需要把mongojs的回调风格方法转换成Promise风格,这样就能用await等待异步操作完成:
const util = require('util'); const mongojs = require('mongojs'); // 把mongojs的查询方法转换成Promise,方便用await const findSubjects = util.promisify(db.subjects.find).bind(db.subjects); // 注意这里用findOne而不是find!因为你是根据单个ID查单个学院,find返回数组,findOne返回单个对象 const findFacultyById = util.promisify(db.faculties.findOne).bind(db.faculties); router.post('/getMySubjects', async function (req, res) { try { if (req.body.type === 'Professor') { // 等待subjects查询完成,拿到结果 const subjects = await findSubjects({ contractorID: req.body.userId }); // 用Promise.all等待所有学院查询完成(并行执行,效率更高) await Promise.all(subjects.map(async (subject) => { // 等待当前subject对应的学院查询完成 const faculty = await findFacultyById({ _id: mongojs.ObjectID(subject.subjectFor_faculty) }); // 给subject添加faculty属性 subject.faculty = faculty; })); // 所有操作都完成了,再发送响应 res.send(subjects); } else { // 处理非Professor类型的请求 res.send([]); } } catch (err) { // 捕获所有异步错误,避免服务器崩溃 console.error('查询出错:', err); res.status(500).send('服务器内部错误'); } });
关键细节:
- 用
async标记路由处理函数,这样内部可以使用await - 用
util.promisify把回调方法转成Promise,也可以手动封装Promise(见方案二) - 把
db.faculties.find()改成findOne(),因为你查的是单个ID对应的唯一学院,这样返回的是单个对象而不是数组,更符合你的需求 - 用
Promise.all并行处理所有学院查询,比逐个await效率更高(所有查询同时发往数据库,不用等前一个完成再查下一个) - 用
try/catch捕获所有异步操作的错误,保证服务器不会因为未处理的错误崩溃
方案二:手动封装Promise(不用util.promisify)
如果不想用util.promisify,可以自己写Promise封装函数:
const mongojs = require('mongojs'); // 封装查询subjects的Promise function getProfessorSubjects(contractorId) { return new Promise((resolve, reject) => { db.subjects.find({ contractorID: contractorId }, (err, subjects) => { if (err) reject(err); else resolve(subjects); }); }); } // 封装查询单个学院的Promise function getSingleFaculty(facultyId) { return new Promise((resolve, reject) => { db.faculties.findOne({ _id: mongojs.ObjectID(facultyId) }, (err, faculty) => { if (err) reject(err); else resolve(faculty); }); }); } router.post('/getMySubjects', async function (req, res) { try { if (req.body.type === 'Professor') { const subjects = await getProfessorSubjects(req.body.userId); await Promise.all(subjects.map(async (subject) => { const faculty = await getSingleFaculty(subject.subjectFor_faculty); subject.faculty = faculty; })); res.send(subjects); } else { res.send([]); } } catch (err) { console.error('查询出错:', err); res.status(500).send('服务器内部错误'); } });
为什么原来的代码不行?
再给你捋一遍执行顺序:
- 执行
db.subjects.find(),发起异步查询 - 数据库返回结果,执行回调函数,把
subjects赋值给data - 执行
data.forEach(),循环里发起多个db.faculties.find()异步查询(这些查询都进入事件队列等待执行) - 立刻执行
res.send(data),此时所有db.faculties.find()的回调都还没跑,subject.faculty根本没被赋值 - 事件队列里的
db.faculties.find()回调开始执行,给subject添加faculty属性,但这时候响应已经发出去了,没人在乎这个修改了
内容的提问来源于stack exchange,提问作者screamvirtuous
相关产品推荐
相关产品推荐

