javascript執行機制:Event Loop

chenhongdong發表於2018-03-28

js是單執行緒的

  • 因為是單執行緒,所以所有任務都需要排隊,前一個任務結束,後一個任務才能執行,如果前一個任務花費時間較長,後一個任務等待時間也隨之變長。
  • js可以做到先把等待中的任務先放一邊晾著,去處理後面的任務。
  • 於是所有任務可以分為兩種,一種是同步任務,另一種是非同步任務:
    • 同步任務很簡單,前面的任務完成後面才能執行,一個接一個的執行任務。
    • 非同步任務不佔用主執行緒,直接進入“任務佇列”中,等任務佇列通知主執行緒,某個任務可以執行了,才會進入主執行緒執行。

非同步執行的執行機制

  1. 所有同步任務都在主執行緒上執行,形成一個執行棧
  2. 主執行緒之外,還存在一個“任務佇列”。只要非同步任務有了執行結果,就在“任務佇列”中放置一個事件
  3. 一旦執行棧中的所有同步任務都執行完畢,就會去“任務佇列”中讀取新的任務放到執行棧中,再依次執行任務
  4. 只要主執行緒空了,就會讀取任務佇列,這就是js的執行機制。這個過程會不斷的重複
    主執行緒和任務佇列.jpg

再說說事件和回撥函式

  • 任務佇列其實存放的是事件的佇列,主程式讀取任務佇列,其實就是在讀有哪些事件罷了
  • 只要指定過回撥函式,這些事件發生時就會進入任務佇列中,等待主執行緒讀取
  • 非同步任務必須指定回撥函式,當主執行緒執行非同步任務時,其實就是在執行對應的回撥函式
  • 任務佇列是一個先進先出的資料結構,排在前面的先執行。當呼叫棧中的任務空了後,主執行緒會自動呼叫任務佇列裡的任務執行

來看看Event Loop

  • 主執行緒從任務佇列中讀取任務,這個過程是不斷重複的,所以被稱為Event Loop(事件迴圈),從字面意思就清楚了
  • 再來看一張圖
    瀏覽器中的Event Loop.png

上圖中,主執行緒產生了heap(堆)和stack(棧),棧中的程式碼呼叫各種api,然後在任務佇列中加入click,load,done等事件,當棧中的任務都執行完後就去呼叫任務佇列中的事件並依次執行

function one() {
    var a = 1;
    two();
    function two() {
        var b = 2;
        three();
        function three() {
            console.log(b);
        }
    }
}

one();
// 棧:是先進後出 函式呼叫就是最常見的形式
// one -> two -> three 依次執行
// 銷燬過程是three -> two -> one
複製程式碼

任務佇列中的定時器

console.log(1);
setTimeout(function () {
    console.log('定時器');
},0);      // 如果不寫時間,預設是4ms
let p = new Promise(function (resolve, reject) {
    console.log(3);
    resolve(100);
}).then(function (data) {
    console.log(data);
});
console.log(2);
// 1 3 2 100 '定時器' 
複製程式碼

Node

先來看看node是如何工作的

node工作流程.png

NodeJs的執行機制:

  1. V8引擎解析js程式碼
  2. 程式碼中可能會呼叫node API,node會交給LIBUV庫處理
  3. LIBUV通過阻塞I/O和多執行緒實現了非同步I\O
  4. 將任務的執行結果返回給V8引擎,V8引擎再將結果返回給使用者

Node中的Event Loop

在LIBUV內部有這樣一個事件環機制,在node啟動時會初始化事件環

node的Event Loop.png

  • 這裡每一個階段都對應一個事件佇列,當Event Loop執行到某一階段的時候會將該階段對應的事件依次執行。
  • 當佇列執行完畢or執行的數量超過上限的時候,會自動轉入下一階段。 這裡我們重點關注一下poll階段

poll階段

poll階段.png

總結一下:以上內容就是關於事件環的整體流程,也可以理解一下同步和非同步的區別

相關文章