對(duì)微任務(wù)和宏任務(wù)的執(zhí)行順序的理解
首先給一段例子:
js 是單線程執(zhí)行的,js中的任務(wù)按順序一個(gè)一個(gè)的執(zhí)行,但是一個(gè)任務(wù)耗時(shí)太長(zhǎng);
那么后面的任務(wù)就需要等待,為了解決這種情況,將任務(wù)分為了同步任務(wù)和異步任務(wù);
而異步任務(wù)又可以分為微任務(wù)和宏任務(wù)。
代碼示例:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
返回結(jié)果的打印順序是:
script start
script end
promise1
promise2
setTimeout
具體為什么會(huì)打印出這個(gè)順序?
我們具體看一下js的執(zhí)行流程:

解讀:
1)、同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場(chǎng)所",同步的進(jìn)入主線程,異步的進(jìn)入Event Table并注冊(cè)函數(shù)
2)、當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。
3)、主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。
4)、上述過(guò)程會(huì)不斷重復(fù),也就是常說(shuō)的Event Loop(事件循環(huán))。
那么主線程內(nèi)的任務(wù)執(zhí)行為空的判定?
在js引擎中,存在一個(gè)叫monitoring process的進(jìn)程,這個(gè)進(jìn)程會(huì)不斷的檢查主線程的執(zhí)行情況,一旦為空,就會(huì)去Event Quene檢查有哪些待執(zhí)行的函數(shù)。
宏任務(wù)和微任務(wù)的分類
宏任務(wù):
setTimeout setInterval
js主代碼
setImmediate(Node) requestAnimationFrame(瀏覽器)
微任務(wù):
process.nextTick Promise的then方法
微任務(wù) 和 宏任務(wù)的執(zhí)行分析
微任務(wù)和宏任務(wù)的問(wèn)題應(yīng)該是前端面試中比較常見(jiàn)的,他們都從屬于異步任務(wù),主要區(qū)別在于他們的執(zhí)行順序,Event Loop的走向和取值

這張圖的意思就是:
1)、存在微任務(wù)的話,那么就執(zhí)行所有的微任務(wù) 2)、微任務(wù)都執(zhí)行完之后,執(zhí)行下一個(gè)宏任務(wù) 3)、1, 2以此循環(huán)著 對(duì)于微任務(wù)的執(zhí)行順序上也有些需要注意的地方
基本上,若你喜歡異步任務(wù)盡可能快地執(zhí)行,那就使用process.nextTick
根據(jù)語(yǔ)言規(guī)格,Promise對(duì)象的回調(diào)函數(shù),會(huì)進(jìn)入異步任務(wù)里面的”微任務(wù)“(microtask)隊(duì)列。
微任務(wù)隊(duì)列追加在process.nextTick隊(duì)列的后面。也屬于本輪循環(huán)。所以,下面的代碼總是先輸出3,再輸出4
process.nextTick(()=>console.log(3))
promise.resolve().then(()=>console.log(4))
//輸出為 //3 //4
此時(shí)需要注意,只有前一個(gè)隊(duì)列完全清空后,才會(huì)執(zhí)行下一個(gè)隊(duì)列
process.nextTick(()=>console.log(1))
promise.resolve().then(()=>console.log(2))
process.nextTick(()=>console.log(3))
promise.resolve().then(()=>console.log(4))
//輸出為 //1 //3 //2 //4 //上面的代碼中,process.nextTick的回調(diào)會(huì)早執(zhí)行于Promise
以下是一段多層次的代碼
這時(shí),我們可以通過(guò)以下代碼進(jìn)行一個(gè)微任務(wù)和宏任務(wù)的執(zhí)行順序練習(xí)
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')
})
})
第一輪循環(huán):
1)、首先打印 1
2)、接下來(lái)是setTimeout是異步任務(wù)且是宏任務(wù),加入宏任務(wù)暫且記為 setTimeout1
3)、接下來(lái)是 process 微任務(wù) 加入微任務(wù)隊(duì)列 記為 process1
4)、接下來(lái)是 new Promise 里面直接 resolve(7) 所以打印 7 后面的then是微任務(wù) 記為 then1
5)、setTimeout 宏任務(wù) 記為 setTimeout2
第一輪循環(huán)打印出的是 1 7
當(dāng)前宏任務(wù)隊(duì)列:setTimeout1, setTimeout2
當(dāng)前微任務(wù)隊(duì)列:process1, then1,
第二輪循環(huán):
1)、執(zhí)行所有微任務(wù)
2)、執(zhí)行process1,打印出 6
3)、執(zhí)行then1 打印出8
4)、微任務(wù)都執(zhí)行結(jié)束了,開始執(zhí)行第一個(gè)宏任務(wù)
5)、執(zhí)行 setTimeout1 也就是 第 3 - 14 行
6)、首先打印出 2
7)、遇到 process 微任務(wù) 記為 process2
8)、new Promise中resolve 打印出 4
9)、then 微任務(wù) 記為 then2
第二輪循環(huán)結(jié)束,當(dāng)前打印出來(lái)的是 1 7 6 8 2 4
當(dāng)前宏任務(wù)隊(duì)列:setTimeout2
當(dāng)前微任務(wù)隊(duì)列:process2, then2
第三輪循環(huán):
1)、執(zhí)行所有的微任務(wù)
2)、執(zhí)行 process2 打印出 3
3)、執(zhí)行 then2 打印出 5
4)、執(zhí)行第一個(gè)宏任務(wù),也就是執(zhí)行 setTimeout2 對(duì)應(yīng)代碼中的 25 - 36 行
5)、首先打印出 9
6)、process 微任務(wù) 記為 process3
7)、new Promise執(zhí)行resolve 打印出 11
8)、then 微任務(wù) 記為 then3
第三輪循環(huán)結(jié)束,當(dāng)前打印順序?yàn)椋? 7 6 8 2 4 3 5 9 11
當(dāng)前宏任務(wù)隊(duì)列為空
當(dāng)前微任務(wù)隊(duì)列:process3,then3
第四輪循環(huán):
1)、執(zhí)行所有的微任務(wù)
2)、執(zhí)行process3 打印出 10
3)、執(zhí)行then3 打印出 12
代碼執(zhí)行結(jié)束:
最終打印順序?yàn)椋? 7 6 8 2 4 3 5 9 11 10 12
原文地址 https://zhuanlan.zhihu.com/p/257069622
