我们把同步任务和异步任务进行细分为宏任务
和微任务
。
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
第一次循环,从上到下,遇到主线程就执行,遇到宏任务就先放进宏任务队列中等待执行,遇到微任务就放到为任务队列中等待执行。第一次宏任务循环结束,处理第一次循环中的微任务,第一次循环完全结束。
开始第二次循环,从第一次循环中的等待的宏任务队列中执行,如果宏任务中包含宏任务或微任务,就开始第三次循环,第二次循环待定。
例子:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
})
从上到下开始执行,第一次循环。
遇到console.log
,直接输出1
;
遇到setTimeout
宏任务,进入宏任务队列,我们记为setTimeout1
;
遇到process.nextTick
微任务,进入微任务队列,记为process.nextTick1
;
遇到new promise
,立即执行,输出7
;
遇到then
微任务,进入微任务队列,记为then1
;
又遇到setTimeout
宏任务,进入宏任务队列,记为setTimeout2
。
第一次循环宏任务结束(整体script结束)。
现在
- 宏任务队列:setTimeout1、setTimeout2
- 微任务队列:process.nextTick1、then1
执行微任务:
process.nextTick1
:输出6
。
then1
:输出8
;
第一次循环微任务结束。
第一次循环结束。
第二次循环:
从第一次循环的宏任务队列中开始执行:
执行setTimeout1
,
遇到console.log
,直接输出2
;
遇到process.nextTick
微任务,进入微任务队列,记为process.nextTick2
;
遇到new promise
,立即执行,输出4
;
遇到then
微任务,进入微任务队列,记为then2
;
第二次循环宏任务结束(setTimeout1结束)。
现在
- 宏任务队列:setTimeout2
- 微任务队列:process.nextTick2,then2
执行微任务:
process.nextTick2
:输出3
;
then2
:输出5
;
第二次循环微任务结束。
第二次循环结束。
第三次循环:
从第二次循环的宏任务队列中开始执行:
执行setTimeout2,
遇到console.log
,直接输出9
;
遇到process.nextTick
微任务,进入微任务队列,记为process.nextTick3
;
第三次循环宏任务结束(setTimeout2结束)。
现在
- 宏任务队列:
- 微任务队列:process.nextTick3
执行微任务:
process.nextTick3
:输出10
;
第三次循环微任务结束。
第三次循环结束。
完整输出为:1 7 6 8 2 4 3 5 9 10
因为我没有node.js的环境,所以使用process.nextTick()会报错,然后我们把代码中的process.nextTick()全部去掉,就是不输出3 6 10,在js中运行代码。
跟我们上面说的顺序是一样的。
可以参考:
https://juejin.im/post/6844903512845860872
https://www.xingmal.com/article/article/1274329692991787008