理解瀏覽器和nodeJs中的事件迴圈(Event Loop)

是我放火燒了雨發表於2018-04-09

瀏覽器環境下 js 引擎的事件迴圈機制

js 引擎每次只能執行一個操作,而通常情況下操作又不止一個,因此這些操作會被依次放入一個佇列中,js 引擎會按照佇列中的順序去執行操作,這個佇列叫做執行棧。
當執行棧中要執行非同步任務時,js 引擎會暫時將它掛起,繼續執行執行棧中的其他任務。
當非同步任務返回結果後,js 引擎會將事件加入到與當前執行棧不同的另一個佇列,我們稱之為事件佇列。
js 引擎不會立即執行事件佇列裡的任務,而是執行棧中的所有任務執行完,主執行緒處於閒置狀態時,js引擎才會把事件佇列裡的事件對應的回撥拿出來放到執行棧中執行。
如此反覆,就形成了無限迴圈,這就是稱之為“事件迴圈”的原因。
其實,非同步任務的執行順序也是有優先順序的。我們把非同步任務分為兩類:微任務(micro task)和巨集任務(macro task)
複製程式碼

微任務如:

new Promise()

new MutaionObserver()
複製程式碼

巨集任務如:

setInterval()

setTimeout()

setImmediate()
複製程式碼

  

 如果你理解了下面程式碼的先後輸出也就理解了瀏覽器的事件環。

setTimeout(function(){
    console.log('setTimeout1');
  Promise.resolve().then(function(){
    console.log('Promise')
  })
})
setTimeout(function(){
  console.log('setTimeout2');
})
複製程式碼

瀏覽器的輸出為:setTimeout1 Promise setTimeout2(不懂promise的請看我的上一篇隨筆~)

node的輸出為:setTimeout1 setTimeout2 Promise
複製程式碼

node.js下的事件迴圈機制

Node的“事件迴圈”(Event Loop)是它能夠處理大併發、高吞吐量的核心。這是最神奇的地方,據此Node.js基本上可以理解成“單執行緒”,同時還允許在後臺處理任意的操作。

Node採用單執行緒的處理機制,對所有的I/O請求採用非堵塞的工作方式。也就是說,對於http的請求,資料查詢,檔案的I/O等其他操作,系統不會堵塞當前執行緒而去等待他直到他返回結果;
複製程式碼

下面來看下node事件迴圈包含的幾個階段:

1. timers:執行setTimeout()和setInterval安排的回撥

2. I/O callbacks: 執行幾乎所有異常的close回撥,由timer和setImmediate執行的回撥。idle,prepare: 只用於內部

3. poll : 獲取新的I/O事件,node在該階段會適當的阻塞

4. check : setImmediate的回撥被呼叫

5. close callbacks 階段: 比如socket.on(‘close’, callback)的callback會在這個階段執行

在每次執行事件迴圈之間,node.j檢查是否有正在等待的非同步i/o呼叫、timers等。如果沒有,就清除並結束(退出程式),例如:執行一個程式,
僅有一句話(let a= ‘hello’;),處理完目的碼後,不會進入evetloop,而是直接結束程式。
poll階段在node.js裡,除了上面幾個特定階段的callback之外,任何非同步方法完成時,都會將其callback加到poll queue裡。
setInterval都是在poll 階段執行完當前的I/O佇列中相應的回撥函式後觸發的。
但是這兩個函式卻是由不同的路徑觸發的
process.nextTick()不在event loop的任何階段執行,而是在各個階段切換的中間執行,即從一個階段切換到下個階段前執行。
process.nextTick()是node早期版本無setImmediate時的產物,node作者推薦我們儘量使用setImmediate。
複製程式碼

以上是本人對這兩方面的見解,僅供參考。

相關文章