事件循環簡單理解


原地址: https://zhuanlan.zhihu.com/p/87684858

關於執行中的線程:

主線程:也就是 js 引擎執行的線程,這個線程只有一個,頁面渲染、函數處理都在這個主線程上執行。
工作線程:也稱幕后線程,這個線程可能存在於瀏覽器或js引擎內,與主線程是分開的,處理文件讀取、網絡請求等異步事件。

任務隊列( Event Queue )

所有的任務可以分為同步任務和異步任務,同步任務,顧名思義,就是立即執行的任務,同步任務一般會直接進入到主線程中執行;而異步任務,就是異步執行的任務,比如ajax網絡請求,setTimeout 定時函數等都屬於異步任務,異步任務會通過任務隊列的機制(先進先出的機制)來進行協調。具體的可以用下面的圖來大致說明一下:

 

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

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

1.在此次 tick 中選擇最先進入隊列的任務( oldest task ),如果有則執行(一次)

2.檢查是否存在 Microtasks ,如果存在則不停地執行,直至清空Microtask Queue

3.更新 render

4.主線程重復執行上述步驟

可以用一張圖來說明下流程:

 

 

 

 

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

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

setTimeout/Promise 等API便是任務源,而進入任務隊列的是由他們指定的具體執行任務。來自不同任務源的任務會進入到不同的任務隊列。其中 setTimeout 與 setInterval 是同源的。

舉例

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');

整體 script 作為第一個宏任務進入主線程,遇到 console.log,輸出 script start
遇到 setTimeout,其回調函數被分發到宏任務 Event Queue 中
遇到 Promise,其 then函數被分到到微任務 Event Queue 中,記為 then1,之后又遇到了 then 函數,將其分到微任務 Event Queue 中,記為 then2
遇到 console.log,輸出 script end

至此,Event Queue 中存在三個任務:宏任務:setTimeout 微任務:then1、then2

執行微任務,首先執行then1,輸出 promise1, 然后執行 then2,輸出 promise2,這樣就清空了所有微任務
執行 setTimeout 任務,輸出 setTimeout 至此,輸出的順序是:script start, script end, promise1, promise2, setTimeout

 

由上面可理解:

 

 執行第一個宏任務: 整體代碼 輸出了script start, script end

根據上圖

 

 

 執行完宏任務需要執行所有微任務, 所以then先執行輸出promise1, promise2, , 最后再進行宏任務setTimeout


免責聲明!

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



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