javascript與nodejs的事件環
nodejs的event是基於libuv,而瀏覽器的event loop則在html5的規範中明確定義。
瀏覽器中的事件環
- 所有同步任務都在主執行緒上執行,形成一個執行棧
- 主執行緒之外,還存在一個任務佇列。只要非同步任務有了執行結果,就在任務佇列之中放置一個事件。
- 一旦執行棧中的所有同步任務執行完畢,系統就會讀取任務佇列,將佇列中的事件放到執行棧中依次執行
- 主執行緒從任務佇列中讀取事件,這個過程是迴圈不斷的
console.log(1)
setTimeout(function(){
console.log(2)
setTimeout(function(){
console.log(3)
})
})
setTimeout(function(){
console.log(4)
})
console.log(5)
複製程式碼
- 列印結果:15243
- 存放在任務佇列中的事件有:onclick,onload,ajax,setTimeout,setInterval等非同步方法
- 執行棧中的方法總是在任務佇列前執行,任務佇列中的執行順序是先進先出
nodejs中的事件環
Node.js也是單執行緒,但是它的event loop執行機制不同於瀏覽器環境。
- 我們寫的js程式碼會交給v8引擎進行處理
- 程式碼中可能會呼叫nodeApi,node會交給libuv庫處理
- libuv通過阻塞i/o和多執行緒實現了非同步io
- 通過事件驅動的方式,將結果放到事件佇列中,最終交給我們的應用。
nodejs中事件迴圈的幾個階段 在libuv內部有這樣一個事件環機制。在node啟動時會初始化事件環
┌───────────────────────┐
┌─ │ timers(計時器) │
| | 執行setTimeout以及 |
| | setInterval的回撥。 |
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks |
│ | 處理網路、流、tcp的錯誤 |
| | callback |
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
| | node內部使用 |
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐ ┌───────────────┐
│ │ poll(輪詢) │ │ incoming: │
| | 執行poll中的i/o佇列 | ─────┤ connections, │
| | 檢查定時器是否到時 | │ data, etc. |
│ └──────────┬────────────┘ └───────────────┘
│ ┌──────────┴────────────┐
│ │ check(檢查) │
| | 存放setImmediate回撥 |
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks |
│ 關閉的回撥例如 |
| sockect.on('close') |
└───────────────────────┘
複製程式碼
階段總覽
- timers:執行setTimeout()和setInterval安排的回撥
- I/O callbacks: 執行幾乎所有異常的close回撥,由timer和setImmediate執行的回撥。
- idle,prepare: 只用於內部
- poll : 獲取新的I/O事件,node在該階段會適當的阻塞
- check : setImmediate的回撥被呼叫
- close callbacks: e.g socket.on(‘close’,…);
- 在每次執行事件迴圈之間,node.j檢查是否有正在等待的非同步i/o呼叫、timers等。如果沒有,就清除並結束(退出程式),例如:執行一個程式,僅有一句話(var a= ‘hello’;),處理完目的碼後,不會進入evetloop,而是直接結束程式。
階段詳解
- timers,定時器階段: 執行定時任務(setTimeOut(), setInterval())
- setTimeout 和 setImmediate 二者非常相似,但是二者區別取決於他們什麼時候被呼叫.
- setImmediate 設計在poll階段完成時執行,即check階段;
- setTimeout 設計在poll階段為空閒時,且設定時間到達後執行;但其在timer階段執行 其二者的呼叫順序取決於當前event loop的上下文,如果他們在非同步i/o callback之外呼叫(在i/o內呼叫因為下一階段為check階段),其執行先後順序是不確定的,需要看loop的執行前的耗時情況
- poll階段
poll 輪詢階段:
- 處理到期的定時器任務,然後(因為最開始階段佇列為空,一旦佇列為空,就會檢查是否有到期的定時器任務)
- 處理佇列任務,直到佇列空,或達到上限
- 如果佇列為空:如果setImmediate,終止輪詢階段,進入檢查階段執行。如果沒setImmediate,檢視有沒有定時器任務到期,有的話就到timers階段,執行回撥函式.
- check階段
poll階段變為空閒、等待狀態時,一旦呼叫setImmediate(),eventloop會進入check 階段,而不是在poll階段等待。
- close callbacks階段
例如:socket或控制程式碼關閉,close事件會觸發這個階段。或者通過process.nextTick()觸發