每一個代理都是由 事件循環驅動的,事件循環負責收集用事件(包括用戶事件以及其餘非用戶事件等)、對任務進行排隊以便在合適的時候執行回調。而後它執行全部處於等待中的 JavaScript 任務(宏任務),而後是微任務,而後在開始下一次循環以前執行一些必要的渲染和繪製操做。
這裏的任務隊列指的是事件循環開始迭代時的任務隊列html
在事件循環開始迭代以後加入到隊列中的任務須要在下一次迭代開始以後纔會被執行。
Macrotasks
一般指的是Tasks
(下文有介紹),Macrotasks
在標準上是查不到任何相關描述的,這個概念從何而來不得而知,可是業界廣泛認可這個概念以跟Microtasks作區分。web
而Microtasks
是寫入HTML Standard的標準,而且在瀏覽器環境有相關api queueMicrotask。api
實際上瀏覽器中只有任務(Tasks)和微任務(Microtasks)之分,下面來說講他們的概念和區別。promise
一個 任務 就是由執行諸如從頭執行一段程序、執行一個事件回調或一個 interval/timeout 被觸發之類的標準機制而被調度的任意 JavaScript 代碼。這些都在 任務隊列(task queue)上被調度。
在如下時機,任務會被添加到任務隊列:瀏覽器
- 一段新程序或子程序被直接執行時(好比從一個控制檯,或在一個
<script>
元素中運行代碼)。- 觸發了一個事件,將其回調函數添加到任務隊列時。
- 執行到一個由
setTimeout()
或setInterval()
建立的 timeout 或 interval,以至相應的回調函數被添加到任務隊列時。
當調用setTimeout(fn, time)
和setInterval(fn, time)
添加任務時,第二個參數time
指的是任務被添加到任務隊列的時間,而不是fn
被執行的時間,fn
被執行的時間應該是被添加的任務執行的時間。app
一個 微任務(microtask)就是一個簡短的函數,當建立該函數的函數執行以後, 而且只有當 Javascript 調用棧爲空,而控制權還沒有返還給被 user agent 用來驅動腳本執行環境的事件循環以前,該微任務纔會被執行。 事件循環既多是瀏覽器的主事件循環也多是被一個 web worker 所驅動的事件循環。這使得給定的函數在沒有其餘腳本執行干擾的狀況下運行,也保證了微任務能在用戶代理有機會對該微任務帶來的行爲作出反應以前運行。
JavaScript 中的 promises 和 Mutation Observer API 都使用微任務隊列去運行它們的回調函數,但當可以推遲工做直到當前事件循環過程完結時,也是能夠執行微任務的時機。爲了容許第三方庫、框架、polyfills 能使用微任務,Window
暴露了queueMicrotask()
方法,而Worker
接口則經過WindowOrWorkerGlobalScope
mixin 提供了同名的 queueMicrotask() 方法。
當你想在一次任務結束以前執行一些代碼時(而不是下一次任務再執行),你可使用微任務,具體的使用場景能夠看看什麼時候使用微任務。框架
- 當執行來自任務隊列中的任務時,在每一次新的事件循環開始迭代的時候運行時都會執行隊列中的每一個任務。在每次迭代開始以後加入到隊列中的任務須要在下一次迭代開始以後纔會被執行。
- 每次當一個任務執行上下文爲空的時候,微任務隊列中的每個微任務會依次被執行。不一樣的是它會等到微任務隊列爲空纔會中止執行——即便中途有微任務加入。換句話說,微任務能夠添加新的微任務到隊列中,並在當前任務結束以前且下一個任務開始執行以前執行完全部的微任務。
在寫這篇文章的時候我有一個疑問,既然在一個任務執行上下文爲空的時候,再執行微任務隊列中的微任務。那微任務是在當前被執行任務離開任務隊列以前執行仍是以後執行?這就關係到一個任務的執行時間應不該該包括微任務的執行時間,以及事件循環的執行順序。webapp
我查了一些資料,在HTML Standard和ECMA標準裏好像並無明肯定義任務和微任務隊列應該是包含關係仍是相互獨立的關係,因此任務和微任務之間的關係應該是瀏覽器廠商本身去實現的。ide
我這裏只測試了Chrome瀏覽器(版本號是87.0.4280.66)函數
能夠看到Chrome是把Run Microtasks
的執行時間算在了Task
的執行時間裏的,因此在Chrome環境裏任務和微任務是包含關係,也就是說當一個微任務執行時,一定有一個任務在同時執行。