JS是單執行緒引擎,線上程中擁有唯一一個事件迴圈(web workder涉及到了多執行緒,再做補充)

JS程式碼執行過程中,除了依靠函式呼叫棧順序執行JS程式碼,還依靠任務佇列(task queue)執行一些程式碼

一個執行緒中,事件迴圈是唯一的,但是任務佇列可以有多個

任務佇列分為macro-task(巨集任務)micro-task(微任務),新標準中稱為task和jobs。

macro-task包括script(程式碼整體)、setTimeout、setInterval、IO、UI rendering。

micro-task包括process.nextTick、Promise、MutationObserver(HTML5新特性)。

setTimeout/Promise為任務源,進入任務佇列的是他們的執行任務(如回撥函式)。

執行規則:
  • 來自不同任務源的任務會進入到不同的任務佇列。其中setTimeout與setInterval是同源的(?)。
  • 事件迴圈的順序,決定了JavaScript程式碼的執行順序。它從script(整體程式碼)開始第一次迴圈。之後全域性上下文進入函式呼叫棧。直到呼叫棧清空(只剩全域性),然後執行所有的micro-task。當所有可執行的micro-task執行完畢之後。迴圈再次從macro-task開始,找到其中一個任務佇列執行完畢,然後再執行所有的micro-task,這樣一直迴圈下去。
  • 其中每一個任務的執行,無論是macro-task還是micro-task,都是藉助函式呼叫棧來完成。
非同步執行的執行機制如下:
  1. 所有同步任務都在主執行緒上執行,形成一個執行棧(execution context stack)。
  2. 主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了執行結果,就在"任務佇列"之中放置一個事件。
  3. 一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
  4. 主執行緒不斷重複上面的第三步。主執行緒不斷重複上面的第三步。

兩個過程圖解: 事件迴圈機制0

事件迴圈機制1

有些部落格中寫的是script執行到比如setTimeout就放入事件佇列中,這個說的有問題,是在你定義的timeout時間之後,將回調函式放入事件佇列中,所謂macroTask microTask是早期為了區分promise,process.netxTick等和其他如setTimeout執行順序不一樣而定義的,標準中並沒有macroTask這麼一說。

參考: