在 JavaScript 引擎中,任務分為兩種類型:微任務(microtask)和宏任務(macrotask)。 微任務是指在當前任務執(zhí)行結(jié)束后立即執(zhí)行的任務,它可以看作是在當前任務的“尾巴”添加的任務。常見的微任務包括 Promise 回調(diào)和 process.nextTick。 宏任務是指需要排隊等待 JavaScript 引擎空閑時才能執(zhí)行的任務。常見的宏任務包括 setTimeout、setInterval、I/O 操作、DOM 事件等。
JS中微任務和宏任務執(zhí)行順序
1.首先執(zhí)行當前代碼(同步任務),直到遇到第一個宏任務或微任務。
2.如果遇到微任務,則將它添加到微任務隊列中,繼續(xù)執(zhí)行同步任務。
3.如果遇到宏任務,則將它添加到宏任務隊列中,繼續(xù)執(zhí)行同步任務。
4.當前任務執(zhí)行完畢后,JavaScript 引擎會先執(zhí)行所有微任務隊列中的任務,直到微任務隊列為空。
5.然后執(zhí)行宏任務隊列中的第一個任務,直到宏任務隊列為空。
重復步驟 4 和步驟 5,直到所有任務都被執(zhí)行完畢。 需要注意的是,微任務比宏任務優(yōu)先級要高,因此在同一個任務中,如果既有微任務又有宏任務,那么微任務會先執(zhí)行完畢。而在不同的任務中,宏任務的執(zhí)行優(yōu)先級要高于微任務,因此在一個宏任務執(zhí)行完畢后,它才會執(zhí)行下一個宏任務和微任務隊列中的任務。 舉個例子,假設當前代碼中有一個 setTimeout 和一個 Promise,它們分別對應一個宏任務和一個微任務。那么執(zhí)行順序如下:
1). 執(zhí)行當前代碼,將 setTimeout 和 Promise 添加到宏任務和微任務隊列中。
2). 當前任務執(zhí)行完畢,JavaScript 引擎先執(zhí)行微任務隊列中的 Promise 回調(diào)函數(shù)。
3). 微任務隊列為空后,再執(zhí)行宏任務隊列中的 setTimeout 回調(diào)函數(shù)。 需要注意的是,在一些特殊情況下,微任務和宏任務的執(zhí)行順序可能會發(fā)生變化,比如在使用 MutationObserver 監(jiān)聽 DOM 變化時,它會被視為一個微任務,但是它的執(zhí)行順序可能會比其他微任務更靠后。因此,需要根據(jù)具體情況來理解和處理微任務和宏任務的執(zhí)行順序。
宏任務有哪些
宏任務包括但不限于以下幾種常見的任務:
1、定時器任務: 如setTimeout、setInterval
2、I/O任務:例如網(wǎng)絡請求、文件讀寫等需要進行I/O操作的任務
3、用戶交互任務:例如點擊事件、輸入事件等與用戶交互的相關任務
4、渲染任務:當瀏覽器需要重繪或重新布局時觸發(fā)的任務
5、請求動畫幀任務:通過requestAnimationFrame()方法設置的任務,用于在每一幀進行繪畫或動畫操作
這些任務都是比較耗時的操作,在事件循環(huán)中被視為宏任務,需要等待一定時間或特定的觸發(fā)條件才會執(zhí)行
微任務有哪些
1、Promise回調(diào):Promise對象的resolve或reject方法的回調(diào)函數(shù)
2、MutationObserver回調(diào):當DOM發(fā)生變化時觸發(fā)的回調(diào)函數(shù)
3、Promise的then()回調(diào):Promise對象的then()方法中的回調(diào)函數(shù)
4、async/await函數(shù)中的后續(xù)操作:在async函數(shù)中使用await等待的操作完成后,緊接著的代碼塊中的任務
這些任務通常是較小且輕量級的操作,執(zhí)行時間較短,適合在當前宏任務執(zhí)行完畢后立即執(zhí)行。由于微任務的執(zhí)行時機在每個宏任務執(zhí)行的過程中,因此可以保證在用戶交互之前或渲染之前得到及時處理
示例,以下代碼console.log的輸出順序
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');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');

1.執(zhí)行當前任務,輸出 script start
2.接著遇到setTimeout,放到宏任務隊列
3.調(diào)用async1函數(shù),輸出async1 start
4.接著走到await async2(), 輸出async2,把await放到微任務隊列
5.接著遇到promise,輸出promise1,then方法放到微任務隊列
6.接著執(zhí)行當前任務,輸出script end
7.到此,當前任務執(zhí)行完畢,開始依次執(zhí)行微任務隊列
8.await async2執(zhí)行完畢,輸出async1 end
9.繼續(xù)執(zhí)行下一個微任務.then,輸出promise2
10.微任務執(zhí)行完畢,開始執(zhí)行宏任務隊列中的定時函數(shù),輸出setTimeout
const promise1 = new Promise((resolve, reject)=> {
console.log(4)
resolve()
}).then(res=> {
console.log(7)
}).then(res=> {
console.log(1)
})
console.log(8)
setTimeout(()=>{console.log(0)},0)
const promise2 = Promise.resolve().then(res=> {
console.log(3)
}).then(res=> {
console.log(6)
})
//4 8 7 3 1 6 0