Nodejs事件循環(huán)

事件循環(huán)原理
1、node 的初始化* 初始化 node 環(huán)境。* 執(zhí)行輸入代碼。* 執(zhí)行 process.nextTick 回調(diào)。* ????
? ? ? 執(zhí)行 microtasks。
2、進(jìn)入 event-loop* 進(jìn)入 timers 階段* 檢查 timer 隊(duì)列是否有到期的 timer 回調(diào),如果有,
? ? ?將到期的 timer 回調(diào)按照 timerId 升序執(zhí)行。* 檢查是否有 process.nextTick 任務(wù),如果? ? ? ? ? ?有,全部執(zhí)行。* 檢查是否有microtask,如果有,全部執(zhí)行。* 退出該階段。* 進(jìn)入IO? ? ? ? ? ? ?callbacks階段。* 檢查是否有 pending 的 I/O 回調(diào)。如果有,執(zhí)行回調(diào)。如果沒有,退出? ? ? ? ?該階段。* 檢查是否有 process.nextTick 任務(wù),如果有,全部執(zhí)行。* 檢查是否有? ? ? ? ? ? ? ? ? ? ?microtask,如果有,全部執(zhí)行。* 退出該階段。* 進(jìn)入 idle,prepare 階段:* 這兩個(gè)階段? ? ? ? ?與我們編程關(guān)系不大,暫且按下不表。* 進(jìn)入 poll 階段* 首先檢查是否存在尚未完成的回? ? ? ? ?調(diào),如果存在,那么分兩種情況。* 第一種情況:* 如果有可用回調(diào)(可用回調(diào)包含到期的? ? ? ?定時(shí)器還有一些IO事件等),執(zhí)行所有可用回調(diào)。* 檢查是否有 process.nextTick 回調(diào),? ? ? ? ?如果有,全部執(zhí)行。* 檢查是否有 microtaks,如果有,全部執(zhí)行。* 退出該階段。* 第二? ? ? ? ?種情況:* 如果沒有可用回調(diào)。* 檢查是否有 immediate 回調(diào),如果有,退出 poll 階段。? ? ? ? ?如果沒有,阻塞在此階段,等待新的事件通知。* 如果不存在尚未完成的回調(diào),退出poll階? ? ? ?段。* 進(jìn)入 check 階段。* 如果有immediate回調(diào),則執(zhí)行所有immediate回調(diào)。* 檢查是否? ? ? ?有 process.nextTick 回調(diào),如果有,全部執(zhí)行。* 檢查是否有 microtaks,如果有,全部執(zhí)? ? ? ?行。* 退出 check 階段* 進(jìn)入 closing 階段。* 如果有immediate回調(diào),則執(zhí)行所有? ? ? ? ? ? ? ? ? ? ?immediate回調(diào)。* 檢查是否有 process.nextTick 回調(diào),如果有,全部執(zhí)行。* 檢查是否有? ? ? ?microtaks,如果有,全部執(zhí)行。* 退出 closing 階段* 檢查是否有活躍的 handles(定時(shí)? ? ? ? ? ?器、IO等事件句柄)。* 如果有,繼續(xù)下一輪循環(huán)。* 如果沒有,結(jié)束事件循環(huán),退出程? ? ? ? ?序。
細(xì)心的童鞋可以發(fā)現(xiàn),在事件循環(huán)的每一個(gè)子階段退出之前都會(huì)按順序執(zhí)行如下過程:
????檢查是否有 process.nextTick 回調(diào),如果有,全部執(zhí)行。
????檢查是否有 microtaks,如果有,全部執(zhí)行。
????退出當(dāng)前階段。
????也就是,在每個(gè) tick 階段都會(huì)執(zhí)行 微任務(wù)隊(duì)列,先執(zhí)行 process.nextTick 隊(duì)列,在執(zhí)行? ? ? ? ? Promise 隊(duì)列
階段一:timers 定時(shí)器
定時(shí)器階段會(huì)執(zhí)行 setTimeout() 和 setInterval() 兩個(gè)回調(diào)函數(shù),在這個(gè)階段主線程會(huì)檢查當(dāng)前時(shí)間是否滿足定時(shí)器的條件,如果滿足就執(zhí)行,不滿足會(huì)跳過進(jìn)入下一個(gè)階段,如果在下一個(gè)階段阻塞了,那么再進(jìn)入定時(shí)器執(zhí)行時(shí),時(shí)間可能就不那么準(zhǔn)確了。
階段二:pending callbacks
pending callbacks 意為掛起的回調(diào)函數(shù),此階段對(duì)某些系統(tǒng)操作(如 TCP 錯(cuò)誤類型)執(zhí)行回調(diào)。例如,如果 TCP 套接字在嘗試連接時(shí)接收到 ECONNREFUSED,則某些 *nix 的系統(tǒng)希望等待報(bào)告錯(cuò)誤。這將被排隊(duì)以在 掛起的回調(diào)階段執(zhí)行。
以下回調(diào)函數(shù)排除
setTimeout()和setInterval()的回調(diào)函數(shù)
setImmediate()的回調(diào)函數(shù)
用于關(guān)閉請(qǐng)求的回調(diào)函數(shù),比如socket.on('close', ...)
階段三:idle, prepare
該階段僅系統(tǒng)內(nèi)部(libuv)調(diào)用
階段四:poll
檢索新的 I/O 事件;執(zhí)行與 I/O 相關(guān)的回調(diào)(幾乎所有情況下,除了關(guān)閉的回調(diào)函數(shù),setImmediate() 之外),其余情況 node 將在此處阻塞。
階段五:check
setImmediate() 回調(diào)函數(shù)在這里執(zhí)行。
階段六:close callbacks
一些準(zhǔn)備關(guān)閉的回調(diào)函數(shù),如:socket.on('close', ...)。