Express开发中MongoDB查询回调函数延后执行的原因
根本原因
这是Node.js异步非阻塞I/O模型的正常表现,本质是JS运行时的任务调度机制导致的:
- Mongoose的所有数据库操作(
countDocuments/find等)都是异步任务,你传入的回调函数不会在调用数据库方法时同步执行 - 执行
Account.find()时,JS主线程会把数据库查询请求交给底层I/O线程处理,自身不会等待查询结果,会直接往下执行后续的同步代码——也就是你写在外层的console.log和res.render逻辑 - 等数据库查询完成、返回结果后,传入的回调函数才会被放进事件队列,等当前同步栈全部执行完才会被调度执行,所以你会看到回调内的日志晚于外层打印。外层执行
res.render时prof还没被赋值,自然拿不到正确的图片地址。
另外你现有代码存在冗余:先调用countDocuments判断账号存在,再调用find查全量数据,两次数据库查询完全可以合并为一次findOne查询,减少不必要的性能损耗。
修复方案
所有依赖异步查询结果的逻辑,必须放到异步回调内部执行,修正后代码如下:
app.post("/login", function(req, res) { const name = req.body.username; const pass = req.body.password; // 合并两次查询,直接获取匹配的单条用户数据 Account.findOne({username: name, password: pass}, function(err, data) { // 优先处理数据库错误 if (err) { console.log("数据库查询异常", err); return res.render("login", { error: "登录失败,请稍后重试" }); } // 无匹配账号 if (!data) { console.log("no account"); return res.render("login", { error: "You dont have a account !" }); } // 拿到查询结果后再执行页面渲染 const prof = data.profile; console.log(prof + " 执行顺序正常"); res.render("index", { url: prof }); }) })
提示:除了回调写法,你也可以用Mongoose原生支持的Promise链式调用,或者async/await语法让异步代码的执行顺序看起来更接近同步逻辑,减少回调嵌套,但核心原则不变:必须等异步操作返回结果后,再执行依赖该结果的逻辑。
内容的提问来源于stack exchange,提问作者PRANESH
相关产品推荐
相关产品推荐

