理解JS中的Event Loop機制

acdseen發表於2018-04-06

前言

前幾天在理解node的事件環機制中引發了我對瀏覽器裡Event Loop的好奇。我們都知道javascript是單執行緒的,任務是需要一個一個按順序執行的,如果javascript有兩個執行緒,一個為DOM增加樣式,一個卻要刪除DOM,這樣豈不是就會很混亂。單執行緒可以節約記憶體,但是必須等待前一個任務完成後才能執行下一個任務。接下來理解瀏覽器中的Event Loop,先看一張圖:

理解JS中的Event Loop機制

heap(堆)和stack(棧)

heap(堆)是使用者主動請求而劃分出來的記憶體區域,比如你new Object(),就是將一個物件存入堆中,可以理解為heap存物件。 stack(棧)是由於函式執行而臨時佔用的記憶體區域,函式都存放在棧裡。

Event Loop實現過程

在上一張圖中:

  1. 所有同步任務都在主執行緒上執行,形成一個執行棧;
  2. 只要非同步任務有了執行結果,就在任務佇列(task queue)(佇列是一個先進先出的資料結構,而棧是一個先進後出的資料結構)之中放置一個事件;
  3. 一旦執行棧中的所有同步任務執行完畢,系統就會讀取任務佇列,又將佇列中的事件放到stack中依次執行,就是執行非同步任務中的回撥函式。這個過程是迴圈不斷的,這就是Event Loop(事件迴圈);

巨集任務和微任務

在上張圖中,我們看到還有巨集任務(MacroTask)和微任務(MicroTask)之分。

巨集任務(MacroTask) setTimeout setInterval

微任務(MicroTask) Promise.then MessageChannel微任務(vue中nextTick實現原理)

同步任務先執行,遇到微任務 就將微任務放入執行棧 微任務會先執行,再執行巨集任務,

先看一下圖(個人理解)

理解JS中的Event Loop機制

舉例說明

console.log(1);
setTimeout(function(){
    console.log(2);
    new Promise(function(resolve,reject){
        console.log('promise');
        resolve();
    }).then(res=>{
        console.log('promise.then');
    })
});
setTimeout(function(){
        console.log(4);
    })
console.log(5);
複製程式碼

將這行程式碼放入瀏覽器控制檯中

理解JS中的Event Loop機制

分析一下:

  • 執行棧中同步任務先執行,先走console.log(1)和console.log(5);
  • 接著是遇到setTimeout將它們的回撥函式放入MacroTask(巨集任務佇列);
  • 然後將任務佇列中的回撥函式依次放入主執行棧中執行:console.log(2)執行,接著promise立即執行,然後promise.then是微任務放入MicroTask中直接入棧先執行;
  • 最後執行第二個setTimeout的回撥函式console.log(4);

Node.js的Event Loop

瀏覽器中的Event Loop和node的Event Loop有所不同,先來看看區別:

理解JS中的Event Loop機制

在node環境裡,執行棧會先執行完當前任務佇列,也就是兩個setTimeout中的回撥函式執行完才會去執行我們的微任務佇列,也就是promise.then是最後執行的,是不是很奇怪。

請看下面的示意圖(作者 @BusyRich)。

理解JS中的Event Loop機制

這裡需要注意一下,node新加了一個微任務(process.nextTick)和一個巨集任務(setImmediate)

簡單的來說,就是node在處理一個執行佇列的時候不管怎樣都會先執行完當前佇列,然後再清空微任務佇列,再去執行下一個佇列。

廢話不多說,直接上圖(個人理解)。

理解JS中的Event Loop機制

這裡應該都明白了吧,最後注意一下,微任務中process.nextTick比promise.then快

理解JS中的Event Loop機制

水平不足,歡迎各位指正。

參考資料

JavaScript 執行機制詳解:再談Event Loop

相關文章