直接上代码
console.log('1. 同步代码开始');
process.nextTick(() => {
console.log('2. nextTick 1');
process.nextTick(() => console.log('3. nextTick 1 内部的 nextTick'));
});
setTimeout(() => {
console.log('4. setTimeout 0');
process.nextTick(() => console.log('5. setTimeout 0 内部的 nextTick'));
}, 0);
setImmediate(() => {
console.log('6. setImmediate');
process.nextTick(() => {
console.log('7. setImmediate 内部的 nextTick');
setImmediate(() => console.log('8. setImmediate 内部的 setImmediate'));
});
});
const { port1, port2 } = new MessageChannel();
port1.onmessage = () => {
console.log('9. MessageChannel 消息');
process.nextTick(() => console.log('10. MessageChannel 内部的 nextTick'));
};
port2.postMessage('触发');
Promise.resolve().then(() => {
console.log('11. Promise resolve 微任务');
process.nextTick(() => console.log('12. Promise 内部的 nextTick'));
setTimeout(() => console.log('13. Promise 内部的 setTimeout'), 0);
});
queueMicrotask(() => {
console.log('14. queueMicrotask');
process.nextTick(() => console.log('15. queueMicrotask 内部的 nextTick'));
});
new Promise((resolve) => {
console.log('16. Promise 构造函数同步代码');
resolve('17. Promise 解析值');
}).then((value) => {
console.log(value);
return new Promise((resolve) => {
console.log('18. 第二个 then 中的 Promise 构造函数');
setImmediate(() => {
console.log('19. 内层 Promise 的 setImmediate');
resolve('20. 内层 Promise 解析值');
});
});
}).then((value) => {
console.log(value);
process.nextTick(() => console.log('21. 第三个 then 中的 nextTick'));
});
setTimeout(() => {
console.log('22. setTimeout 0 (第二个)');
process.nextTick(() => {
console.log('23. 第二个 setTimeout 内部的 nextTick');
Promise.resolve().then(() => console.log('24. nextTick 内部的微任务'));
});
}, 0);
setImmediate(() => {
console.log('25. setImmediate (第二个)');
process.nextTick(() => console.log('26. 第二个 setImmediate 内部的 nextTick'));
});
console.log('27. 同步代码结束');
解析过程
现在的AI很方便得到答案,但是不要过于相信AI给的答案,AI也是很容易出错的
- 同步代码执行,输出1
- 将nextTick回调,放入nextTick队列
- 将setTimeout回调,放入宏任务队列
- 将setImmediate回调,放入宏任务队列
- 将onmessage回调,放入宏任务队列
- 将promise.then的回调放入微任务队列
- 将queueMicrotask回调放入微任务队列
- 同步代码执行Promise的立即执行函数,输出16
- 将.then方法回调放入微任务队列
- setTimeout回调放入宏任务队列
- setImmediate放入宏任务队列
- 同步代码执行,输出27
同步代码执行完毕,现在有三个任务队列,nextTick队列,微任务队列,宏任务队列
- 执行nextTick队列里面的任务,一次性全部取出nextTick任务
- 执行,输出2
- 产生新的nextTick回调,放入nextTick队列
- 再取出nextTick队列里面的任务执行
- 输出3,nextTick队列清空
nextTick代码执行完毕,现在去处理微任务队列
- 一次性全部取出微任务队列的任务,放到执行栈
- 输出11, 产生一个nextTick任务,放入nextTick队列,产生一个宏任务,放入宏任务队列
- 输出14, 产生一个nextTick任务,放入nextTick队列
- 输出17,
- Promise内部代码立即执行, 输出18,产生一个宏任务,放入宏任务队列
- 此时微任务队列已清空,并且没有产生新的微任务
微任务代码执行完毕,现在nextTick任务队列有任务,继续处理nextTick的任务
- 输出12
- 输出15
nextTick执行完毕,没有微任务,现在开始处理宏任务,宏任务一个一个取出执行的,现在的宏任务有三种,setTimeout,messageChange, setImmediate,并且优先级是从大到小(备注:这里是node执行环境,在node18版本及以后,setTimeout优先级高于messageChange,16版本及以前正好相反)
- 我的node版本是22,所以先执行setTimeout
- 输出4, 产生一个nextTick任务
- 第一个宏任务执行完毕,处理nextTick任务
- 输出5
- 取出第二个宏任务执行,输出22,产生一个nextTick任务
- 取出nextTick任务执行,输出23,产生一个微任务
- 取出微任务执行,输出24
- 取出第三个宏任务执行,输出13
- setTimeout宏任务全部执行完毕,取出messageChannel宏任务执行
- 输出9,产生一个nextTick任务
- 取出nextTick任务执行,输出10
- setTimeout和messageChannel都执行完毕了,执行setImmediate
- 输出6, 产生一个nextTick任务
- 取出nextTick任务执行,输出7,产生一个宏任务,放入宏任务队列
- 取出下一个宏任务执行,输出25,产生一个nextTick任务
- 取出nextTick任务执行,输出26,
- 取出下一个宏任务执行, 输出19, 产生一个微任务,放入微任务队列
- 取出微任务执行,输出20, 产生一个nextTick任务,放入nextTick队列
- 取出nextTick任务执行,输出21
- 取出最后一个宏任务执行, 输出8
总结
- setTimeout,messageChannel,setImmediate是有优先级顺序的,在node16版本以前和16版本以后,优先级不一样
- 在node中nextTick的优先级比微任务更高,并且它和微任务一样,是一次性全部取出执行,执行过程中,又产生了nextTick,继续取出执行
- 微任务的执行,是一次性全部取出执行,执行过程中如果又产生了微任务,继续执行
- 宏任务是一个一个取出执行