js的異步任務分2類:宏任務(macrotask )和微任務(microtask )
什麼是宏任務、微任務
網上用食堂排隊打飯或者銀行排隊辦業務舉例,個人認爲不太恰當,他們並不是包含或嵌套關係。
js是單線程腳本語言,在需要執行異步任務時,就需要瀏覽器協助完成,形成一套事件循環機制(event loop)。
瀏覽器在完成js交給的異步任務後,會在js的回調隊列中插入一個任務,等待js的同步任務執行完成後調用執行,比如計時器、網絡請求等等,
雖說異步任務都放在等待隊列中,但還是有區別的,分宏任務和微任務(猜測按優先級劃分?),js在調用時,優先取出微任務,並且在執行過程中如果創建了新的作業,則放在本次執行完後緊接着調用,微任務執行完成後,再取出宏任務執行。
我也來舉個栗子:
你們幾個人去飯店喫飯,點餐後你們要先開一局王者榮耀,打到一半有個人要上廁所,但一致要他打完這局才能去;打完後菜已上齊但他要先去廁所,剩下的人就要等他回來後才能開飯。
(上廁所就是微任務,很急,喫飯就是宏任務,等緊急事情辦法才能開始,整個過程就是一次事件循環)
宏任務、微任務有哪些
宏任務包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任務包括: Promises, Object.observe, MutationObserver
宏任務、微任務的執行順序
- 先執行同步代碼,
- 遇到異步宏任務則將異步宏任務放入宏任務隊列中,
- 遇到異步微任務則將異步微任務放入微任務隊列中,
- 當所有同步代碼執行完畢後,再將異步微任務從隊列中調入主線程執行,
- 微任務執行完畢後再將異步宏任務從隊列中調入主線程執行,
- 一直循環直至所有任務執行完畢。
實例1
setTimeout(function(){
console.log('1');
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3');
});
console.log('4');
- 遇到setTimout,異步宏任務,放入宏任務隊列中;
- 遇到new Promise,Promise在實例化的過程中所執行的代碼都是同步進行的,所以輸出2;
- 而Promise.then中註冊的回調纔是異步執行的,將其放入微任務隊列中
- 遇到同步任務console.log(‘4’);輸出4;主線程中同步任務執行完
- 從微任務隊列中取出任務到主線程中,輸出3,微任務隊列爲空
- 從宏任務隊列中取出任務到主線程中,輸出1,宏任務隊列爲空,結束~
輸出:2 4 3 1
實例2
setTimeout(()=>{
new Promise(resolve =>{
resolve();
}).then(()=>{
console.log('test');
});
console.log(4);
});
new Promise(resolve => {
resolve();
console.log(1)
}).then( () => {
console.log(3);
Promise.resolve().then(() => {
console.log('before timeout');
}).then(() => {
Promise.resolve().then(() => {
console.log('also before timeout')
})
})
})
console.log(2);
- 遇到setTimeout,異步宏任務,將() => {console.log(4)}放入宏任務隊列中;
- 遇到new Promise,Promise在實例化的過程中所執行的代碼都是同步進行的,所以輸出1;
- 而Promise.then中註冊的回調纔是異步執行的,將其放入微任務隊列中
- 遇到同步任務console.log(2),輸出2;主線程中同步任務執行完
- 從微任務隊列中取出任務到主線程中,輸出3,此微任務中又有微任務,Promise.resolve().then(微任務a).then(微任務b),將其依次放入微任務隊列中;
- 從微任務隊列中取出任務a到主線程中,輸出 before timeout;
- 從微任務隊列中取出任務b到主線程中,任務b又註冊了一個微任務c,放入微任務隊列中;
- 從微任務隊列中取出任務c到主線程中,輸出 also before timeout;微任務隊列爲空
- 從宏任務隊列中取出任務到主線程,此任務中註冊了一個微任務d,將其放入微任務隊列中,接下來遇到輸出4,宏任務隊列爲空
- 從微任務隊列中取出任務d到主線程 ,輸出test,微任務隊列爲空,結束~
輸出:1 2 3 before timeout also before timeout 4 test