簡介
我們在上一篇 《淺析 JS 中的EventLoop 事件循環》 中提到一個 Event Queue
,其實在事件循環中 queue 一共有兩種,還有一種叫 Job Queue
其中
Event Queue
在 HTML 規範中被稱爲Task Queue
,但是爲了區分,一般都叫作Macrotask Queue
Job Queue
是在 ECMAScript 規範中談及處理 Promise 回調時提到的,但是由於和 V8 中的實現比較相似,所以一般都稱爲Microtask Queue
Macrotask
Macrotasks 包含了解析 HTML、生成 DOM、執行主線程 JS 代碼和其他事件如 頁面加載、輸入、網絡事件、定時器事件等。從瀏覽器的角度,Macrotask 代表的是一些離散的獨立的工作。
常見應用setTimeout
, setInterval
, setImmediate
, requestAnimationFrame
, I/O
, UI rendering
Microtask
Microtasks 則是爲了完成一些更新應用程序狀態的較小的任務,如處理 Promise 的回調和 DOM 的修改,以便讓這些任務在瀏覽器重新渲染之前執行。Microtask 應該以異步的方式儘快執行,所以它們的開銷比 Macrotask 要小,並且可以使我們在 UI 重新渲染之前執行,避免了不必要的 UI 渲染。
常見應用process.nextTick
, Promises
, Object.observe
, MutationObserver
執行順序
Event Loop 的實現需要至少一個 Macrotask Queue 和至少一個 Microtask Queue。爲了便於理解,我們都簡化成一個。
簡單來說,Microtask Queue 具有更高的優先級,即執行一個 Macrotask 任務後,就會清空整個 Microtask Queue,此時如果有新的 Microtask 加入也會被執行。
所以我們來看下面的代碼:
執行順序是什麼?
我們已經知道 setTimeout 是 Macrotask,Promise 是 Microtask,而這段代碼從上到下執行也是一個 Macrotask
步驟:
- 開始執行,執行腳本作爲一個任務進入 Macrotask Queue,同時進入調用棧執行
- Line 1, 輸出
script start
- Line 3 的 setTimeout 回調進入 Macrotask Queue 等待
- Line 7 的回調進入 Microtask Queue 等待
- Line 13 輸出
script end
,此時腳本執行完成(即完成了一個 Macrotask) - 開始執行 Microtask Queue,從中拿出一個放入調用棧執行
- 開始執行 Line 7 的回調,該回調輸出
promise1
,返回 undefined - Line 9 的回調進入 Microtask Queue,由於 Microtask Queue 沒有清空,直接執行該回調,輸出
promise2
,該回調返回 undefined - Microtask Queue 已清空(此時瀏覽器可以更新渲染UI),開始將 Macrotask Queue 中任務放入調用棧執行
- 執行 Line 3 的回調,輸出
setTimeout
,Macrotask Queue 清空 - 程序執行完成
所以打印順序爲:
script start -> script end -> promise1 -> promise2 -> setTimeout
PS. 上面的這段代碼執行流程,建議看原文的倒數第二篇參考文章,有動態交互操作可以演練。
總結
- Microtask 相比 Macrotask 具有更高的優先級
- Macrotask 總是在 JS 代碼執行完成並且 Microtask Queue 清空之後執行
- JS 代碼執行本身也是一個 Macrotask
- Microtask Queue 清空後有可能會重新渲染 UI
- Promise 屬於 Microtask,setTimeout 屬於 Macrotask
總體的執行順序爲:常規代碼
-> promises
-> events 和 setTimeout 等
參考文章
原文鏈接
ECMA262 Job Queues
HTML Standard Task Queue
HTML系列:macrotask和microtask
microtask and macrotask a hands on approach
difference-between-microtask-and-macrotask-within-an-event-loop-context
公衆號:碼力全開