Node.js路由代码重构求助:res.render提前执行致数据无法渲染
解决异步forEach导致res.render提前执行的问题
这个问题我之前也踩过坑!核心原因就是JavaScript的forEach不支持等待内部的异步操作完成——你在forEach里写的async回调函数会被丢进事件队列排队,主线程却直接跳过循环执行后面的console.log("second log")和res.render,所以才会出现你看到的日志顺序,模板自然拿不到if块里更新的status和marks。
下面给你两种靠谱的重构方案,按需选择:
方案一:用for...of替代forEach(串行执行,逻辑直观)
for...of是支持和await配合的,它会等待每次循环里的异步操作完成后,再进入下一次循环,确保所有元素都更新完毕后才执行res.render:
router.get('/', ensureLogin, async (req, res) => { try { let result = await Mock.find(); // 替换forEach为for...of,支持await等待异步操作 for (const e of result) { if (e.attemptedBy.includes(req.user._id)) { let a_Mock = await User.findOne( { _id: req.user._id, "attemptedMock.setNo": e.setNo }, { attemptedMock: 1 } ); // 这里的更新会同步到result数组里 e.status = "Attempted"; // 加存在性判断,避免找不到对应记录时报错 e.marks = a_Mock?.attemptedMock?.[0]?.totalMarks || 0; console.log(a_Mock?.attemptedMock?.[0]?.totalMarks); } else { e.status = "Unattempted"; console.log('else block') } } console.log("second log"); // 现在result已经全部更新完成,可以放心渲染 res.render('dashboard', { mocks: result }); } catch (e) { console.log(e); } })
方案二:用Promise.all并行处理(效率更高,适合大量数据)
如果你的Mock数据比较多,for...of串行执行会比较慢,可以用Promise.all并行发起所有异步请求,等全部完成后再继续:
router.get('/', ensureLogin, async (req, res) => { try { let result = await Mock.find(); // 用map生成所有异步Promise,再用Promise.all等待全部完成 await Promise.all(result.map(async (e) => { if (e.attemptedBy.includes(req.user._id)) { let a_Mock = await User.findOne( { _id: req.user._id, "attemptedMock.setNo": e.setNo }, { attemptedMock: 1 } ); e.status = "Attempted"; e.marks = a_Mock?.attemptedMock?.[0]?.totalMarks || 0; console.log(a_Mock?.attemptedMock?.[0]?.totalMarks); } else { e.status = "Unattempted"; console.log('else block') } })); console.log("second log"); res.render('dashboard', { mocks: result }); } catch (e) { console.log(e); } })
额外提醒
我加了?.可选链操作符和|| 0默认值,是为了避免用户的attemptedMock里没有匹配setNo的项时,出现Cannot read properties of undefined的报错,实际项目里可以根据需求调整默认值或者错误处理逻辑。
内容的提问来源于stack exchange,提问作者Deepak Soni
相关产品推荐
相关产品推荐

