巨集任務、微任務
參考1:https://www.cnblogs.com/lucy-xyy/p/11652286.html
參考2:https://www.cnblogs.com/yugege/p/9598265.html
事件迴圈機制
JavaScript 語言的一大特點就是單執行緒,也就是說,同一個時間只能做一件事,但是一個任務耗時太長,那麼後面的任務就需要等待,為了解決這種情況,將任務分為了同步任務和非同步任務,而非同步任務又可以分為微任務和巨集任務。
同步和非同步任務分別進入不同的執行環境,同步的進入主執行緒,即主執行棧,非同步的進入 Event Queue (任務佇列)。主執行緒內的任務執行完畢為空,會去 Event Queue 讀取對應的任務,推入主執行緒執行。 上述過程的不斷重複就是我們說的 Event Loop (事件迴圈)。
所以非同步操作都是放到事件迴圈佇列裡面,等待主執行棧來執行的,也並沒有專門的非同步執行執行緒。
巨集任務和微任務
task分為兩大類, 分別是 Macro Task (巨集任務)和 Micro Task(微任務), 並且每個巨集任務結束後, 都要清空所有的微任務。
macro task巨集任務主要包含:script( 整體程式碼)、setTimeout、setInterval、I/O、UI 互動事件、setImmediate(Node.js 環境)
micro task微任務主要包含:Promise函式中的then語句、async中的await語句、MutaionObserver、process.nextTick(Node.js 環境)
補充:在 node 環境下,process.nextTick 的優先順序高於 Promise函式中的then語句
從圖中可以看到,巨集任務執行順序優先於微任務,所以JS執行順序為:script>清空微任務>巨集任務>清空微任務>巨集任務>清空微任務……直至清空任務佇列
舉個栗子
async function async1() {
console.log(2); // 順序4.同步程式碼最先執行,輸出2
await fn(); // 順序5:await會阻塞await後面程式碼的執行,但不阻塞await表示式,同時await會跳出async函式
console.log(7); // 順序10:await執行完畢,且await之前不存在微任務,執行後續同步程式碼及await時生成的微任務,輸出7
}
function fn() {
// 順序6:執行fn,輸出3,await跳出async函式讓出執行緒,執行async後面的程式碼
console.log(3);
}
console.log(1); // 順序1.由上而下,同步程式碼最先執行,輸出1
setTimeout(function () { // 順序2.setTimeout為巨集任務,進入任務佇列進行等待執行
console.log(10); // 順序13.無同步程式碼、無微任務,開始執行下一個巨集任務,輸出10
}, 0)
async1(); // 順序3.同步程式碼,執行方法async1
new Promise(function (resolve) {
console.log(4); // 順序7:執行Promise同步程式碼,輸出4,記錄.then為微任務1,進入佇列等待執行
resolve();
}).then(function () {
console.log(8); // 順序11.無同步程式碼,依次執行剩餘微任務,輸出8
});
new Promise(function (resolve) {
console.log(5); // 順序8:執行Promise同步程式碼,輸出5,記錄.then為微任務2,進入佇列等待執行
resolve();
}).then(function () {
console.log(9); // 順序12.無同步程式碼,執行剩餘微任務2,輸出9
});
console.log(6); // 順序9:執行同步程式碼,輸出6