JavaScript事件循環機制及微任務與宏任務


事件循環

事件循環不僅僅包含事件隊列,而是具有至少兩個隊列,除了事件,還要保持瀏覽器執行的其他操作。這些操作被稱為任務,並且分為兩類:宏任務(或通常稱為任務)和微任務。
單次循環迭代中,最多處理一個宏任務(其余的在隊列中等待),而隊列中的所有微任務都會被處理。當微任務隊列處理完成並清空時,事件循環會檢查是否需要更新UI渲染,如果是,則會重新渲染UI視圖。至此,當前事件循環結束。

事件循環基於兩個基本原則:
一次處理一個任務。
一個任務開始后直到運行完成,不會被其他任務中斷。

兩類任務隊列都是獨立於事件循環的,這意味着檢測和添加任務的行為,是獨立於事件循環完成的。
因為JavaScript基於單線程執行模型,所以這兩類任務都是逐個執行的。當一個任務開始執行后,在完成前,中間不會被任何其他任務中斷。除非瀏覽器決定中止執行該任務,例如,某個任務執行時間過長或內存占用過大。
所有微任務會在下一次渲染之前執行完成,因為它們的目標是在渲染前更新應用程序狀態。
瀏覽器通常會嘗試每秒渲染60次頁面,以達到每秒60幀(60 fps)的速度。在頁面渲染時,任何任務都無法再進行修改。如果想要實現平滑流暢的應用,,單個任務和該任務附屬的所有微任務,都應在16ms內完成。

宏任務

宏任務的例子很多,包括創建主文檔對象、解析HTML、執行主線(或全局)JavaScript代碼,更改當前URL以及各種事件,如頁面加載、輸入、網絡事件和定時器事件。從瀏覽器的角度來看,宏任務代表一個個離散的、獨立工作單元。運行完任務后,瀏覽器可以繼續其他調度,如重新渲染頁面的UI或執行垃圾回收。

常見的宏任務有:setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering.

宏任務場景實例

主線程JavaScript代碼執行時間需要15ms。
第一個單擊事件處理器需要運行8ms。
第二個單擊事件處理器需要運行5ms。

假設用戶在代碼執行后5ms時單擊第一個按鈕,隨后在12ms時單擊第二個按鈕。

執行情況:
1、[0ms]執行主線程
2、[5ms]第一個單擊事件加入隊列,不影響主線程
3、[12ms]第二個單擊事件加入隊列,不影響主線程
4、[15ms]主線程結束,從隊列中移除。執行第一個單擊事件
5、[23ms]第一個單擊事件結束,從隊列中移除。執行第二個單擊事件
6、[28ms]第二個單擊事件結束,從隊列中移除。隊列為空

微任務

而微任務是更小的任務。微任務更新應用程序的狀態,但必須在瀏覽器任務繼續執行其他任務之前執行,瀏覽器任務包括重新渲染頁面的UI。微任務的案例包括promise回調函數、DOM發生變化等。微任務需要盡可能快地、通過異步方式執行,同時不能產生全新的微任務。微任務使得我們能夠在重新渲染UI之前執行指定的行為,避免不必要的UI重繪,UI重繪會使應用程序的狀態不連續。

常見的微任務有:process.nextTick, Promises, Object.observe, MutationObserver。
許多框架底層都有它的痕跡。比如在Vue2.4之前中,Vue.$nextTick()中將回調存儲在微任務中。

微任務場景實例

主線程JavaScript代碼執行時間需要15ms。
第一個單擊事件處理器需要運行8ms。
包含立即兌現的promise,並需要運行4ms的傳入回調函數。
第二個單擊事件處理器需要運行5ms。

假設用戶在代碼執行后5ms時單擊第一個按鈕,隨后在12ms時單擊第二個按鈕。

執行情況:
1、[0ms]執行主線程
2、[5ms]第一個單擊事件加入隊列,不影響主線程
3、[12ms]第二個單擊事件加入隊列,不影響主線程
4、[15ms]主線程結束創建但不執行第一個單擊事件。立即兌現promise,進入微任務隊列。
5、[23ms]從微任務隊列挑選任務並執行,檢查微任務隊列,微任務都執行完畢后。執行第一個單擊事件,從隊列中移除。
6、[27ms]微任務隊列為空,事件循環重新處理宏任務,執行第二個單擊事件。
7、[32ms]第二個單擊事件結束,從隊列中移除。隊列為空.

微任務隊列中含有微任務,不論隊列中等待的其他任務,微任務都將獲得優先執行權。

參考文獻:
《Secrets of the JavaScript Ninja》
https://html.spec.whatwg.org/multipage/webappapis.html#task-queue


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM