關於事件這一塊在《深刻淺出的nodejs》中不多講到,書裏面只是在第三章說起了4個API方法,好比兩個定時器(setTimeout和setInterval),process.nextTick()和setImmediate。node
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
setTimeout(() => {
console.log('timer3')
}, 0)
}, 0)
Promise.resolve().then(function() {
console.log('promise3')
})
console.log('start')
複製代碼
瀏覽器中輸出結果:github
start
promise3
timer1
promise1
timer2
promise2
timer3
複製代碼
這個輸出結果的緣由咱們已經在上一篇文章中說明,本章就很少加贅述。c#
在nodejs中,運行卻能獲得不一樣的結果,讓咱們先來過一下node的事件模型。api
start
promise3
timer1
timer2
promise1
promise2
timer3
複製代碼
node的事件循環分爲6個階段promise
源代碼地址有興趣的能夠去看一下源代碼瀏覽器
六個階段的功能以下:異步
當有數據或者鏈接傳入事件循環的時候,先進入的是poll階段,這個階段,先檢查poll queue中是否有事件,有任務就按先進先出的順序執行回調,若是隊列爲空,那麼會先檢查是否有到期的setImmdiate,若是有,將其的callback推入check隊列中,同時還會檢查是否有到期的timer,若是有,將其callback推入到timers隊列中。若是前面二者都爲空,那麼直接進入I/O callback,並執行這個事件的callback。socket
check階段專門用來執行setImmidate的回調函數。async
用於執行close事件的回調函數
用於執行定時器設置的回調函數
用於執行大部分I/O事件的回調函數。
這個鉤子在node的事件循環模型中沒有說起,可是node中有一個特殊的隊列,nextTick queue。在node事件循環進入到下一個階段的時候,都會去檢測nextTick queue中有沒有清空,若是沒有清空,那麼就會去清空nextTick queue中的事件。
在官網的文檔裏面有那麼一段話:
When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.
一、全部同步任務 二、腳本任務中發送的api請求 三、規劃定時器同步任務的生效時間 四、執行process.nextTick()
第一種狀況
一、清空當前循環內的 Timers Queue,清空NextTick Queue,清空Microtask Queue 二、清空當前循環內的 I/O Queue,清空NextTick Queue,清空Microtask Queue 三、進入poll階段 四、清空當前循環內的 Check Queue,清空NextTick Queue,清空Microtask Queue 五、清空當前循環內的 Close Queue,清空NextTick Queue,清空Microtask Queue
第二種狀況
一、先進入poll階段 二、清空當前循環內的 Check Queue,清空NextTick Queue,清空Microtask Queue 三、清空當前循環內的 Close Queue,清空NextTick Queue,清空Microtask Queue 四、清空當前循環內的 Timers Queue,清空NextTick Queue,清空Microtask Queue 五、清空當前循環內的 I/O Queue,清空NextTick Queue,清空Microtask Queue
setTimeout(() => {
console.log('timeout')
}, 0);
setImmediate(() => {
console.log('immediate')
});
複製代碼
直接運行腳本,輸出的結果是
timeout
immediate
複製代碼
當咱們把他放在同一個I/O循環中運行
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout')
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
複製代碼
輸出的結果是
immediate
timeout
複製代碼
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve().then(() => {
console.log('promise')
})
複製代碼
輸出的結果是
nextTick
promise
複製代碼
nodejs中的實現方式:microtask queue的任務經過runMicrotasks將microtask queue中的task放入到nextTick中,因此microtask的任務會在nextTick queue以後執行。
書本中是推薦使用setImmediate(),用戶若是遞歸調用process.nextTick()的時候,會形成I/O被榨乾。而使用setImmediate,只會在check中執行,不至於異步調用的時候沒法執行。