Node.js事件循环中两段代码执行结果不同的原因咨询
为什么两段Node.js代码的异步执行顺序不同?
要搞懂这个差异,得先理清Node.js事件循环的核心阶段和微任务的执行规则:
基础规则铺垫
Node.js事件循环的核心阶段顺序(简化版):
- Timers阶段:执行已到期的
setTimeout/setInterval回调 - I/O回调阶段:处理文件、网络等I/O相关的回调
- Poll阶段:等待新的I/O事件,执行对应回调;若没有待处理事件,会检查是否有
setImmediate回调,存在则直接进入下一个阶段 - Check阶段:执行
setImmediate的回调 - Close回调阶段:处理关闭相关的回调
另外,微任务队列(process.nextTick和Promise.then)会在每个事件循环阶段结束后立即执行,且process.nextTick队列的优先级高于Promise队列。
第一段代码:Express请求回调场景
const express = require('express'); const app = express(); app.get('', (req, res) => { setTimeout(() => console.log('timeout')); setImmediate(() => console.log('immediate')); Promise.resolve().then(() => console.log('promise')); process.nextTick(() => console.log('nextTick')); }) app.listen(3000);
当请求触发回调时,这段代码是在Poll阶段执行的(Express的请求处理属于I/O回调范畴),执行流程:
- 同步创建所有异步回调任务
- 当前Poll阶段执行完毕,优先处理所有微任务:
- 先执行
process.nextTick队列:输出nextTick - 再执行Promise队列:输出
promise
- 先执行
- 微任务处理完成后,因为Poll阶段检测到存在
setImmediate回调,会直接跳转到Check阶段,执行setImmediate回调:输出immediate - 进入下一轮事件循环的Timers阶段,执行到期的
setTimeout回调:输出timeout
最终顺序:nextTick -> promise -> immediate -> timeout
第二段代码:直接执行场景
setTimeout(() => console.log('timeout')); setImmediate(() => console.log('immediate')); Promise.resolve().then(() => console.log('promise')); process.nextTick(() => console.log('nextTick'));
直接运行时,代码在事件循环初始启动阶段执行,流程:
- 同步创建所有异步回调任务(注意:
setTimeout默认延迟0,但Node.js会强制调整为至少1ms) - 同步代码执行完毕,优先处理所有微任务:
- 执行
process.nextTick队列:输出nextTick - 执行Promise队列:输出
promise
- 执行
- 微任务处理完成后,进入事件循环的第一个阶段Timers,此时同步+微任务的执行时间已经超过1ms,
setTimeout回调到期,执行后输出timeout - 后续依次经过I/O、Poll等阶段,最后进入Check阶段执行
setImmediate回调:输出immediate
最终顺序:nextTick -> promise -> timeout -> immediate
核心差异原因
- 第一段代码的回调在Poll阶段触发,执行完微任务后会直接跳转到Check阶段执行
setImmediate,跳过了本轮的Timers阶段 - 第二段代码在初始启动时执行,同步逻辑结束后直接进入Timers阶段,此时
setTimeout已经到期,优先执行后才会到Check阶段处理setImmediate
内容的提问来源于stack exchange,提问作者Đoàn Đức Bảo
相关产品推荐
相关产品推荐

