Node.js Async函数疑问:哪条console.log会先输出?
执行顺序异常的核心原因
[A]先于[B]输出的本质是对async函数执行规则、JS事件循环机制的认知偏差,和调用栈后进先出规则并不冲突。
1. 未加await的async函数不会阻塞外层代码
所有标记为async的函数,调用后会立刻返回一个Promise对象,只有在调用时使用await关键字,才会暂停外层函数的执行,等待当前async函数内部所有逻辑跑完再往下走。
你的代码中makeUser函数里的调用逻辑是这样的:
const res = await u.save() addAddress(res) // 此处没有写await console.log(res); console.log("[A]")
执行到addAddress(res)时,函数内部运行到第一个await(也就是await User.findById(id)这行MongoDB查询操作),就会立刻暂停addAddress本身的执行,把查询IO操作交给底层处理,同时返回一个pending状态的Promise给外层的makeUser。
因为makeUser没有写await等待这个Promise落定,会直接继续往下执行同步代码:先打印res,再打印[A]。等这部分同步代码全部执行完,事件循环才会处理已经排队的异步逻辑:等findById返回结果、追加新地址、保存文档、打印userSaved、最后打印[B],自然就出现了[A]比[B]先输出的结果。
2. 调用栈后进先出规则仅适合同步执行场景
你提到的调用栈LIFO规则,只对同步连续执行的代码生效。一旦代码执行碰到await、Promise.then、定时器这类异步逻辑,异步逻辑的后续执行片段不会留在当前调用栈等待,会被挪到对应的微任务/宏任务队列排队,必须等当前调用栈里所有同步代码全部执行完毕、栈完全清空后,才会被事件循环取出重新压入栈执行。
在你的代码里,addAddress刚执行到第一个await就主动让出了调用栈,并没有在栈里等待执行完成,外层的console.log("[A]")属于当前栈内的同步代码,会优先执行完,不存在“最后调用的函数必须先跑完”的情况。
修正方式
如果需要让[B]在[A]之前输出,只需要在调用addAddress时加上await,显式等待异步函数执行完成即可:
const res = await u.save() await addAddress(res) // 增加await等待异步逻辑执行完 console.log(res); console.log("[A]")
额外小提示:你定义addAddress时形参写的是id,但实际传入的是u.save()返回的完整文档对象,Mongoose内部会自动识别取_id字段做查询,不影响运行,但写法上直接传res._id语义会更清晰。
内容的提问来源于stack exchange,提问作者RND809
相关产品推荐
相关产品推荐

