事件循环:执行同步任务,然后执行微任务,再执行下一个宏任务,如此循环下去。
常见的宏任务:
- script代码块
- setTimeout
- setTimeInterval
- dom
- ajax
- postMessage
常见的微任务:
- process.nextTick
- promise.then catch finally
- async await
通过以下几个例题进行说明
题目一
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})
console.log('script end')
注意:new Promise的代码是同步执行的
分析过程如下图:
图解:先打印 script start,然后将setTimeout加入宏任务队列,继续执行async1函数,接着打印
async1 start,遇到await时,await 右边的函数同步执行,打印出async2,await下面的代码会加入微任务队列(微任务队列的第一条微任务),然后退出async1函数,继续向下来到new Promise,new Promise的代码是同步执行的,所以打印出promise1,遇到resolve,将then加入微任务队列(微任务队列的第二条微任务),然后打印出script end。
到此同步栈执行完毕,去微任务队列执行微任务。
微任务1,打印出async1 end
微任务2,打印出promise2
到此微任务队列清空,再去执行宏任务。
只有一个宏任务,打印出settimeout。
到此,所有任务执行完毕,最终的输出结果如下:
'script start'
'async1 start'
'async2'
'promise1'
'script end'
'async1 end'
'promise2'
'settimeout'
总结:
1. new Promise里的代码是同步执行的
2. async函数调用的时候去函数里面同步执行,遇到await时,await右侧的代码同步执行,await下面的代码会加入微任务队列。
题目二
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');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
这道题目乍一看有点长,但是不要怕,一步一步分析和梳理。
分析过程如下:
第一步,先拆解出同步,和宏任务,微任务,如下图:
上图中依次标出了宏任务和微任务,没有标出的就是同步执行的代码。
先执行同步代码,依次打印出 1 7。
接下来来到微任务队列,由2条微任务
微任务1,打印出6
微任务2,打印出8
微任务执行完毕。
然后执行宏任务,有2个宏任务
宏任务1,执行过程中,产生了2个微任务1-1和微任务1-2
console.log('2')和console.log('4')是宏任务1中的同步任务,先执行,然后再去执行微任务1-1和微任务1-2。所以在宏任务1执行过程中的打印顺序为:2 4 3 5
宏任务1执行完毕,继续执行下一个宏任务2。
与宏任务1同理,先执行绿色框框的代码,再去微任务队列执行微任务2-1和2-2。
宏任务2的打印顺序为:9 11 10 12
到此为止,所有任务全部执行完毕。
最终的打印顺序为:1 7 6 8 2 4 3 5 9 11 10 12
这样一层层分析下来,发现也不是很难。
题目三
const promise = new Promise((resolve, reject) => {
resolve("10")
}).then(res => {
console.log("res1:", res)
return 9
}).then(res => {
console.log("res2:", res)
return 8
}).then(res => {
console.log("res3:", res)
let promise2=new Promise((resolve,reject)=>{
resolve("p2")
}).then(res=>{
console.log(res)
setTimeout(function(){
console.log("setTimeout2")
},0)
})
})
console.log('aaa')
setTimeout(function(){
console.log("setTimeout1")
},0)
const promise1 = new Promise((resolve, reject) => {
console.log("p1")
resolve(989)
}).then(res => {
console.log(res)
return 990
}).then(res=>{
console.log(res)
return 991
}).then(res=>{
console.log(res)
return 0
})
这个题目看起来更复杂了,但是不要怕,还是逐步进行拆解,一点一点分析。
首先进行第一步拆解,只有2条绿色的同步语句,和2个微任务(其余的代码先暂时不考虑)。
依次执行,先打印aaa,再打印p1。同步执行完成,扫描微任务队列,此时微任务队列只要2条,按顺序执行。
微任务1:打印res1:10,然后执行return 9,也就是产生了新的微任务1-1,排在微任务2的后面。
微任务2:打印989。执行return 990,产生了新的微任务2-1,排在微任务1-1的后面。
微任务1和微任务2执行完之后,此时,微任务队列还有2条新加入的微任务1-1和微任务2-1,分别执行。
微任务1-1:打印res2:9,执行return 8,产生了新的微任务1-1-1,加入队列中,排在微任务2-1的后面。
微任务2-1:打印990,执行return 991,产生了新的微任务2-1-1,加入队列中,排在微任务1-1-1的后面。
微任务1-1和微任务2-1执行完之后,微任务队列还有2条新加入的微任务1-1-1和微任务2-1-1,依次执行。
微任务1-1-1:打印res3:8,在执行时又产生了新的微任务1-1-1-1,排在微任务2-1-1的后面。
微任务2-1-1:打印991,没有再产生新的任务。接下来执行新的微任务1-1-1-1。
微任务1-1-1-1:打印p2,然后产生了宏任务2,排在宏任务1的后面。
到这里,所有的微任务都执行完毕,然后再扫描宏任务队列,执行宏任务1和宏任务2.
宏任务1:打印settimeout1。
宏任务2:打印setTimeout2。
至此,所有的任务都执行完了,最终的输出如下:
这个题目的分析过程有点长,嵌套多层,但是一层层分析,发现本质还是事件循环的机制。
上面3个题目都搞懂了,那么遇到同类型的应该都没有问题。
还有很多其他类型的题目,有时间再继续补充.....