node事件循環

事件循環是node處理非阻塞I/O操做的機制。node

node.js啓動後,會初始化事件輪詢;執行腳本,而後啓動事件循環。promise

概覽

下圖是事件循環處理順序的概覽:瀏覽器

每一個階段都有一個FIFO隊列來執行回調。 當事件循環進入一個階段,會把該階段的回調執行完或者達到最大回調數,纔會移動到下一個階段

階段概述

  • timers: 執行setTimeout()setInterval()的回調函數。
  • pending callbacks: 執行延遲到下一次循環的I/O回調。
  • idle, prepare: 僅供內部使用
  • poll: 檢索新的I/O事件;執行I/O相關回調。node將會在此處阻塞。
  • check: setImmediate()回調
  • close callbacks: 一些關閉的回調函數,如:socket.on('close', ...)

階段詳解

  • timers:給出能夠執行回調的最少時間閾值。
  • poll: 有兩個任務: 1)計算應該阻塞和輪詢I/O 2)執行輪poll列裏面的事件

當事件循環進入poll階段,有兩種可能: 1)poll隊列不是空的,那麼事件循環會將隊列裏面的回調一個一個執行,直到隊列用盡,或者達到最高回調數。 2)poll是空的!,則會去檢查有無check階段回調等待,有的話,移動到check階段執行。有無到達時間的timers回調,有的話,進入timer階段執行。若是都沒有,則會停留在這個poll階段,等待回調,而且在有回調進入的時候,當即執行。bash

setImmediate()對比setTimeout()

處在事件循環不一樣階段,執行時機不一樣。 使用 setImmediate()相對於setTimeout() 的主要優點是,若是setImmediate()是在 I/O 週期內被調度的,那它將會在其中任何的定時器以前執行,跟這裏存在多少個定時器無關,由於poll階段以後,直接就是check階段了。異步

process.nextTick()

process.nextTick()從技術上講不是事件循環的一部分,所以並未出如今上圖事件循環中。不管在哪一個階段,process.nextTick()回調都會在當前回調完成後處理。 這樣機制下,如你過遞歸調用process.nextTick(),就會'餓死'正常I/O輪詢裏的回調。socket

代碼示例

const fs = require('fs')
const path = require('path')

console.log('1')

let promise = new Promise(function(resolve){
  console.log('new promise')
  resolve(1)
})

fs.readFile(path.join(__dirname, './ling.js'), () => {
  console.log('readFile')
  process.nextTick(function(){
    console.log('tick insert')
  })
  promise.then(function(){
    console.log('promise insert')
  })
  setTimeout(() => {
    console.log('timeout')
    process.nextTick(function(){
      console.log('tick insert in timer')
    })
  }, 0)
  setImmediate(() => {
    console.log('immediate')
  })
})

fs.readFile(path.join(__dirname, './ling.js'), () => {
  console.log('readFile2')
  setTimeout(() => {
    console.log('timeout2')
  }, 0)
  setImmediate(() => {
    console.log('immediate2')
  })
})


promise.then(function(){
  console.log('promise then1')
  promise.then(function(){
    console.log('promise then2')
    promise.then(function(){
      console.log('promise then3')
    })
  })
})

process.nextTick(function(){
  console.log('tick 1')
  process.nextTick(function(){
    console.log('tick 3')
  })
})
process.nextTick(function(){
  console.log('tick 2')
})

console.log('end')
複製代碼

執行結果函數

1
new promise // 同步執行,只有.then是異步
end // 第一次同步執行完畢
tick 1 // nextTick隊列優先級最高,先一次性清空
tick 2
tick 3
promise then1 // 和nextTick相似,可是優先級沒nextTick高
promise then2 // 也是先一次性清空
promise then3
readFile // 進入事件循環poll階段
tick insert // 執行完poll階段的一個回調以後,就會去檢查nextTick
promise insert // 和 promise的隊列有沒有回調,有的話執行
readFile2 // 執行完上面的再繼續poll階段下一個回調
immediate // 進入check階段
immediate2 // check階段所有執行完
timeout // 進入timers階段
tick insert in timer // tiemrs執行完一個回調,就會去檢查nextTick,和promise
timeout2 // 繼續執行timers階段
複製代碼

和瀏覽器事件循環對比

差別: node的事件循環和瀏覽器的仍是有差別的,特別是node事件循環存在幾個階段。ui

一致: 在處理promise.then 和process.nextTick 這些微任務,規則是同樣的:就是在任什麼時候候,只要處理完一個回調,就會去檢查,有沒有這些微任務,有的話,就一次性執行完畢。再回去繼續原來。spa

相關文章
相關標籤/搜索