JS 是單線程語言。這句話對不對?
按照目前的情況來看,JS 自從支持了?Web Worker
?之后,就不再是單線程語言了,但 Worker 的工作線程與主線程有區別,在 Worker 的工作線程中無法直接操作 DOM、window 對象或大多數瀏覽器 API(如 localStorage),Worker 的全局對象也不再是 window 對象,而是 self。
Worker 中的事件循環與主線程相互獨立,互不影響,但執行順序還是得遵循 JS 的語法規則。
宏任務
宏任務表示執行時間較長的任務,在每次時間循環時只會執行一個宏任務,執行完畢后處理微任務隊列,所有微任務都執行完畢后進入下一個宏任務。
JS 常見宏任務類型:
-
定時器任務:setTimeout / setInterval
-
DOM 事件回調(如 click、scroll)
-
I/O 操作(如文件讀取、網絡請求)
-
瀏覽器用于執行動畫的方法?
requestAnimationFrame
?,執行時機與渲染相關 -
Node.js 環境的?
setImmediate
-
script 標簽內主線程的同步代碼(整體作為一個宏任務)
微任務
微任務表示更輕量的異步任務,當宏任務執行完畢之后立即執行。
JS 常見微任務類型:
-
Promise.then()
?/?Promise.catch()
?/?Promise.finally()
-
瀏覽器監聽 DOM 變化的 API 對象,比如:
MutationObserver
-
手動添加微任務API方法:
queueMicrotask()
-
nodejs 中的?
process.nextTick()
代碼解析
看這么一段代碼:
(function() {console.log(1)setTimeout(() => { console.log(2); });queueMicrotask(() => console.log(3))new Promise(resolve => {console.log(4);setTimeout(() => {resolve();console.log(5);}, 0);Promise.resolve().then(() => console.log(6));console.log(7);}).then(() => {console.log(8);Promise.resolve().then(() => console.log(9));});console.log(10);
})();
分析代碼:
(function() {console.log(1) // 同步任務setTimeout(() => { console.log(2); });queueMicrotask(() => console.log(3))new Promise(resolve => {console.log(4); // 同步任務setTimeout(() => { // 宏任務resolve(); // 宏任務的同步任務console.log(5); // 宏任務中的同步任務}, 0);Promise.resolve().then(() => console.log(6)); // 微任務console.log(7); // 同步任務}).then(() => { // 微任務console.log(8); // 微任務中的同步任務Promise.resolve().then(() => console.log(9)); // 微任務中的微任務});console.log(10); // 同步任務
})();
第一輪
首先同步代碼的宏任務優先級最高,不管微任務還是宏任務,同步代碼都會先執行。
所以上面代碼會優先執行:
console.log(1)
console.log(4);
console.log(7);
console.log(10);
接著開始處理微任務:
queueMicrotask(() => console.log(3))
Promise.resolve().then(() => console.log(6));
微任務處理完,開始執行下一輪宏任務。
第二輪
這一輪中的宏任務只有一個 setTimeout,執行完之后由于沒有微任務隊列,所以直接執行下一輪宏任務。
setTimeout(() => { console.log(2); });
第三輪
這一輪的宏任務中有同步代碼。
setTimeout(() => { // 宏任務resolve(); // 宏任務的同步任務console.log(5); // 宏任務中的同步任務
}, 0);
在執行完?resolve()
?之后,會將 Promise.then 的回調函數放入微任務隊列中,所以在宏任務執行完之后會開始微任務:
then(() => { // 微任務console.log(8); // 微任務中的同步任務Promise.resolve().then(() => console.log(9)); // 微任務中的微任務
})
最終的打印順序
1
4
7
10
3
6
2
5
8
9
執行流程圖
JS 代碼逐行執行,在遇到宏任務時,整個代碼塊丟到宏任務隊列,在遇到微任務時,將微任務丟到本次事件循環中的微任務隊列,本次事件循環執行完之后,再執行微任務隊列中的任務,微任務執行完之后開始下一個宏任務執行。
JS 代碼執行機制:
宏任務執行機制:
寫在最后
JS 中的代碼執行流程永遠都是事件循環機制,這是 JS 的任務調度核心,理解事件循環機制,才能在開發中游刃有余~~
文章轉載自:前端路引
原文鏈接:Web前端入門第 68 問:JavaScript 事件循環機制中的微任務與宏任務 - 前端路引 - 博客園
體驗地址:JNPF快速開發平臺