1. 程式人生 > >JavaScript Event Loop和微任務、宏任務

JavaScript Event Loop和微任務、宏任務

nextTick hub mic mozilla 執行過程 函數調用 所有 git 全局

為什麽JavaScript是單線程?

JavaScript的一大特點就是單線程, 同一時間只能做一件事情,主要和它的用途有關, JavaScript主要是控制和用戶的交互以及操作DOM。註定它是單線程。 假如是多個線程, 一個移除DOM節點,一個新增DOM節點,瀏覽器以誰的為準呢?

什麽是執執行棧呢?

函數的調用就會形成一個棧幀。當執行棧都為空的時候,主線程就會處於空閑狀態。

   function fn2(x, y) {
      return x + y  
   } 
   
   function fn1(z) {
     let a = 10
     return fn2(a, z)
   }
   
   console.log(fn1(5)) // 15

以上代碼: fn1 函數調用時會創建一個執行棧,棧中包含fn1的參數和局部變量。當 fn1 調用 fn2 時, 第二個執行棧就會被創建, 並且壓入到第一個執行棧之前。 棧中包含了 fn2的參數和全局變量。當 fn2執行完返回時,最前面的執行棧就會被彈出。剩下 fn1 函數的調用幀, 當fn1 函數執行完並返回時, 執行棧就空了。

任務隊列

任務隊列主要用戶掛起等待中的任務(異步任務)。
JavaScript是單線程, 意味著所有的任務需要排隊, 前一個任務執行完,才能進行下一個任務。 AJAX就是典型的異步任務,需要調用HTTP線程,然後發送request請求,再是等待服務端的響應。在結果沒有返回執行,後面的代碼是不會執行的,這會給用戶一種網站卡的現象。

因此, JavaScript 分為 同步任務在主線程上排隊執行的任務,也就是前面執行完畢,才能執行下一個的任務。 異步任務是指它不會進行主線程,不會影響後續代碼的執行,而是進入任務隊列,當異步任務執行有了結果,就會在任務隊列中放置一個事件,等主線程空閑(執行棧為空,同步任務執行完畢),通知任務隊列進入主線程執行。

微任務和宏任務

任務隊列中的異步任務分為 微任務宏任務
常見的微任務有: process.nextTickPromiseMutationObserver監聽DOM的變化。

常見的宏任務: setTimeoutsetIntervalsetImmediatescript

中整體的代碼、 I/O操作UI渲染等。

微任務和宏任務的區別:

  • 微任務進入主線程執行是一隊一隊的, 而宏任務進入主線程是一個一個的。
  • 微任務是在主線程空閑時批量執行, 宏任務是在事件循環下一輪的最開始執行

例子: 以下代碼的打印結果

    console.log(1)
    setTimeout(function() {
        console.log(2)
    })
    
    Promise.resolve()
        .then(function() {
            console.log(3)
        })
    
    console.log(4)
    
   // 打印結果: 1 4 3 2

整個的執行過程:

    stack(執行棧)、Micro(微任務)、Macro(宏任務)

    1.初始狀態: stack:[], Micro: [], Macro: [script]。執行棧為空, 微任務為空, 宏任務隊列中有一個整體的 script代碼 
    
    2. 主線程開始執行, 遇到console.log(1), 首先會打印 1 
    
    3. 繼續向下執行,遇到 setTimeout異步任務,就將其加入到Macro(宏任務)隊列中。等待執行 
    
    4. 繼續向下執行, 遇到 Promise.resolve也是一個異步任務,單它是微任務,將其加入 Micro(微任務)隊列中,等待著行 
    
    5. 解析console.log(4), 並且打印4。 當主線程執行完打印的結果依次是 1 和 4。
    
    6. 這時候主線程就會問 任務(異步)隊列,有沒有微任務要執行,將所有的 Micro(微任務)加入執行棧執行, 打印結果 3
    
    7. 微任務執行完了, 就開始下一輪事件循環, 將第一個 Macro(宏任務)壓入執行棧執行, 再次打印 2。 

Event Loop事件循環

技術分享圖片

只要主線程一空閑就會將 "任務隊列中的異步任務"依次壓入執行棧, 這個過程是循環不斷的,所以整個運行機制稱之為 Event Loop(事件循環)。

執行棧中(stack)的代碼(同步任務),總是在讀取"任務隊列"(異步任務)之前執行。

圖示

技術分享圖片

參考文章

  • 並發模型與事件循環 MDN
  • JavaScript 運行機制詳解:再談Event Loop
  • 從一道題淺說 JavaScript 的事件循環

JavaScript Event Loop和微任務、宏任務