为何单独执行axiosTest与在normalTest后执行时,process.nextTick与Promise顺序不同?
Node.js事件循环中process.nextTick与Promise微任务的执行顺序解析
测试代码
const axiosTest = async () => { setImmediate(() => { console.log('immediate axios'); }) const x = axios.get('https://www.google.com'); x.then((r) => console.log('fetch')); const x2 = new Promise(resolve => { resolve('promise axios') }); x2.then(console.log) process.nextTick(() => { console.log('tick axios') }); console.log('stack axios'); } const normalTest = async () => { setImmediate(() => { console.log('immediate'); }) const x = new Promise(resolve => { resolve('promise') }); x.then(console.log) process.nextTick(() => { console.log('tick') }); console.log('stack'); }
不同执行方式的输出
- 执行
normalTest().then(axiosTest);时,输出顺序:
stack tick promise stack axios promise axios tick axios immediate immediate axios fetch
- 单独执行
normalTest()时,输出顺序:
stack tick promise immediate
- 单独执行
axiosTest()时,输出顺序:
stack axios tick axios promise axios immediate axios fetch
疑问解析
你观察到的差异核心在于执行axiosTest的上下文是宏任务还是微任务,Node.js对微任务队列的执行规则在两种场景下有明确区别:
1. 单独执行axiosTest的场景(宏任务上下文)
当在脚本顶层直接调用axiosTest(),函数执行属于宏任务。函数体同步执行完毕后,Node.js进入微任务处理阶段,遵循固定顺序:
- 优先清空所有
process.nextTick队列中的任务(输出tick axios) - 再清空所有Promise微任务队列中的任务(输出
promise axios)
这就是单独执行时tick axios先于promise axios输出的原因。
2. 在normalTest().then(axiosTest)中执行axiosTest的场景(微任务上下文)
normalTest()是async函数,返回一个Promise,它的.then(axiosTest)回调会被加入Promise微任务队列。当normalTest的微任务执行完毕后,axiosTest作为微任务队列中的任务开始执行:
axiosTest中同步resolve的x2的.then回调会被加入当前的Promise微任务队列process.nextTick的回调会被加入单独的nextTick队列
当axiosTest函数体执行完毕后,Node.js会继续处理当前Promise微任务队列中剩余的任务(即x2.then,输出promise axios),直到Promise微任务队列为空,才会去处理nextTick队列中的任务(输出tick axios)。
本质规则
Node.js的微任务分为两个优先级队列:
nextTick队列:优先级最高,但仅在宏任务执行完毕后,才会优先于Promise微任务执行- Promise微任务队列:优先级低于
nextTick,但如果在Promise微任务的执行过程中添加新的nextTick任务,这些任务会被延后,直到当前所有Promise微任务处理完毕后才会执行
内容的提问来源于stack exchange,提问作者Ozan Başkan
相关产品推荐
相关产品推荐

