js事件循環機制和ui渲染 | 前端面試經典

js事件循環機制和ui渲染

事件循環

任務隊列
所有的任務可以分爲同步任務和異步任務,同步任務,顧名思義,就是立即執行的任務,同步任務一般會直接進入到主線程中執行;而異步任務,就是異步執行的任務,比如ajax網絡請求,setTimeout 定時函數等都屬於異步任務,異步任務會通過任務隊列( Event Queue )的機制來進行協調。

同步和異步任務分別進入不同的執行環境,同步的進入主線程,即主執行棧,異步的進入 Event Queue 。主線程內的任務執行完畢爲空,會去 Event Queue 讀取對應的任務,推入主線程執行。 上述過程的不斷重複就是我們說的 Event Loop (事件循環)。

在事件循環中,每進行一次循環操作稱爲tick,通過閱讀規範可知,每一次 tick 的任務處理模型是比較複雜的,其關鍵的步驟可以總結如下:

  1. 在此次 tick 中選擇最先進入隊列的任務( oldest task ),如果有則執行(一次)
  2. 檢查是否存在 Microtasks ,如果存在則不停地執行,直至清空Microtask Queue
  3. 更新 render
  4. 主線程重複執行上述步驟

可以用一張圖來說明下流程:
在這裏插入圖片描述
那麼,什麼是 microtasks ?規範中規定,task分爲兩大類, 分別是 Macro Task (宏任務)和 Micro Task(微任務), 並且每個宏任務結束後, 都要清空所有的微任務,這裏的 Macro Task也是我們常說的 task 。

(macro)task 主要包含:script( 整體代碼)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 環境)

microtask主要包含:Promise、MutaionObserver、process.nextTick(Node.js 環境)

示例:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
  1. 整體 script 作爲第一個宏任務進入主線程,遇到 console.log,輸出 script start
  2. 遇到 setTimeout,其回調函數被分發到宏任務 Event Queue
  3. 遇到 Promise,其 then函數被分到到微任務 Event Queue 中,記爲 then1,之後又遇到了 then 函數,將其分到微任務 Event Queue 中,記爲 then2
  4. 遇到 console.log,輸出 script end

至此,Event Queue 中存在三個任務,如下表:

宏任務 微任務
setTimeout then1
- then2
  1. 執行微任務,首先執行then1,輸出 promise1, 然後執行 then2,輸出 promise2,這樣就清空了所有微任務
  2. 此時,所有的mircotask執行完畢,本輪事件循環結束,UI 開始 render,當 UI render 完畢,開始下一輪事件循環.
  3. 執行 setTimeout 任務,輸出 setTimeout, 至此,輸出的順序是:script start, script end, promise1, promise2, setTimeout

UI渲染

根據HTML Standard,一輪事件循環執行結束之後,下輪事件循環執行之前開始進行 UI render。即:macro-task任務執行完畢,接着執行完所有的micro-task任務後,此時本輪循環結束,開始執行UI render。UI render完畢之後接着下一輪循環。

💬深入理解JavaScript事件循環機制 (原文)

💬Javascript事件循環機制以及渲染引擎何時渲染UI (補充與渲染UI相關)

💬從多線程到Event Loop全面梳理 (擴展閱讀:通過 進程、線程 的角度來解釋單線程的JS爲什麼擁有 異步 的能力)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章