天天看點

Node中的事件環​Eventloop

作者:前端餐廳

Node中的事件環

Node中的事件環​Eventloop
  1. 我們寫的 js 代碼會交給 v8 引擎進⾏處理
  2. 代碼中可能會調⽤ nodeApi,node 會交給 libuv 庫處理
  3. libuv 通過阻塞 i/o 和多線程實作了異步 io
  4. 通過事件驅動的⽅式,将結果放到事件隊列中,最終交給我們的應⽤。
本階段執⾏已經被 setTimeout() 和 setInterval()的排程回調函數。
   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
|          執⾏延遲到下⼀個循環疊代的 I/O 回調。
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
|         僅系統内部使⽤。
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘
| 檢索新的I/O事件;執⾏與 I/O相關的回調     ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
|   setImmediate() 回調函數在這⾥執⾏。  └───────────────┘
│  ┌─────────────┴─────────────┐
│  │           check           │
│  └─────────────┬─────────────┘
|       ⼀些關閉的回調函數
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘
           

這⾥每⼀個階段都對應⼀個事件隊列,當 event loop 執⾏到某個階段時會将目前階段對應的隊列依次執⾏。當該隊列已⽤盡或達到回調限制,事件循環将移動到下⼀階段。

process.nextTick() 從技術上講不是事件循環的⼀部分。優先級⾼于微任務

浏覽器:每執行完畢一個宏任務,會清空微任務 從 node10+ 後執行機制和我們的浏覽器一樣 新的

以前的 node 事件環是每個階段的宏任務都被清空了,才會執行微任務 老的

  • 宏任務
    • timers 隊列 用來放定時器
    • poll 輪訓(處理 i/o 的回調)
    • check 處理 setImmediate
  • 微任務:process.nextTick > promise.then
Promise.resolve().then(()=>{
    console.log('promise')
})

process.nextTick(()=>{ // 這個方法用的多一些
    console.log('nextTick')
})
           

執行流程​

主棧代碼執行完畢-》會檢測 timer 中有沒有回調,全部清空後進入到下一個階段 -》poll(有 i/o 的回調繼續處理)-> 看有沒有 check,如果有就清空

  • 預設如果沒有 check 也沒有 timer,代碼邏輯還有沒執行完的回調,此時這個線程會在 poll 中等待
  • 如果沒有 timer -》 poll(有 i/o 的回調繼續處理)-》 沒有 check 了

poll 階段:

  1. 檢測 Poll 隊列中是否為空,如果不為空則執⾏隊列中的任務,直到逾時或者全部執⾏完畢。
  2. 執⾏完畢後檢測 setImmediate 隊列是否為空,如果不為空則執⾏ check 階段,如果為空則等待時間到達。時間到達後回到 timer 階段
  3. 等待時間到達時可能會出現新的 callback,此時也在目前階段被清空
setTimeout(() => { // 執⾏順序不确定
console.log('settimeout')
})
setImmediate(() => {
console.log('check')
})
           
const fs = require('fs');
fs.readFile('./package.json', () => { // poll階段的下⼀個階段是check
setTimeout(() => { // timer
console.log('settimeout')
 })
setImmediate(() => { // check
console.log('check')
 })
});           

繼續閱讀