事件循環是node處理非阻塞I/O操做的機制。node
當node.js
啓動後,會初始化事件輪詢;執行腳本,而後啓動事件循環。promise
下圖是事件循環處理順序的概覽:瀏覽器
每一個階段都有一個FIFO隊列來執行回調。 當事件循環進入一個階段,會把該階段的回調執行完或者達到最大回調數,纔會移動到下一個階段。setTimeout()
和setInterval()
的回調函數。setImmediate()
回調socket.on('close', ...)
。當事件循環進入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()
,就會'餓死'正常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