Node,js 事件循環(huán)原理(Event loop)
《新時期的Node.js入門》讀書筆記

事件循環(huán)就是一個程序啟動期間運行的死循環(huán),Node代碼雖然運行在單線程中,但仍能支持高并發(fā),就是依靠事件循環(huán)實現(xiàn)的。
用戶在前臺不斷產(chǎn)生事件,背后的循環(huán)(由瀏覽器實現(xiàn))會逐個地處理它們。而JavaScript是單線程的,為了避免一個過于耗時的操作阻塞了其他操作的執(zhí)行,就需要通過異步加回調(diào)的方式解決問題。
Node作為另一種運行時,事件循環(huán)由底層的 libuv 實現(xiàn)。
timers
I/O callbacks
idle prepare
poll
check
incoming: connections, data, etc.
close callbacks
timers
I/O callbacks
idle prepare
poll
check
incoming: connections, data, etc.
close callbacks
上面的圖例中,將事件循環(huán)分成了6個不同的階段,其中每個階段都維護(hù)著一個回調(diào)函數(shù)的隊列,在不同的階段,事件循環(huán)會處理不同的事件:
Timers:用來處理 setTimeOut() 和 setInterval() 的回調(diào)
I/O callbacks:大多數(shù)的回調(diào)方法在這個階段執(zhí)行,除了 timers、close 和 setImmediate 事件的回調(diào)
idle, prepare:僅僅在內(nèi)部使用,我們不管它
Poll:輪詢,不斷檢查有沒有新的 IO 事件,事件循環(huán)可能會在這里阻塞
Check:處理 setImmediate 事件的回調(diào)。
close callbacks:處理一些close相關(guān)的事件,例如socket.on(‘close’, …)
假設(shè)時間循環(huán)進(jìn)入到了某個階段,即使這期間有其他隊列中的事件就緒,也會先將當(dāng)前階段隊列里的全部回調(diào)方法執(zhí)行完畢后,再進(jìn)入到下一個階段
1 timers
這個階段主要用來處理定時器相關(guān)的回調(diào),當(dāng)一個定時器超時后,一個事件就會加入到隊列中,事件循環(huán)會跳轉(zhuǎn)至這個階段執(zhí)行相應(yīng)的回調(diào)函數(shù)
2 IO callbacks
該階段主要是用來執(zhí)行pending callback,例如一個TCP socket執(zhí)行出現(xiàn)了錯誤,在一些 *nix系統(tǒng)下可能希望稍后再處理這類的錯誤,那么這個回調(diào)就會放在IO callback階段來執(zhí)行。
3 poll
這個階段的主要任務(wù)是等待新事件的出現(xiàn)(該階段使用epoll來獲取新的事件),如果沒有,事件循環(huán)可能會在此阻塞
Poll階段主要有兩個步驟如下:
(1)如果有到期的定時器,那么就執(zhí)行定時器的回調(diào)方法
(2)處理poll階段對應(yīng)的事件隊列里面的事件
當(dāng)事件循環(huán)到達(dá)poll階段時,如果這時沒有要處理的定時器的回調(diào)方法,則會進(jìn)行下面的判斷:
(1)如果poll隊列不為空,則事件循環(huán)會按照順序遍歷執(zhí)行隊列中的回調(diào)函數(shù),整個過程是同步的
(2)如果poll隊列為空, 會接著進(jìn)行如下判斷
如果當(dāng)前代碼定義了setImmediate方法,事件循環(huán)會離開poll階段,然后進(jìn)入check階段區(qū)執(zhí)行setImmediate方法定義的回調(diào)方法
如果當(dāng)前代碼沒有定義setImmediate方法,那么事件循環(huán)會進(jìn)入等待狀態(tài),并等待新的事件出現(xiàn),這也是該階段為什么會被命名為poll(輪詢)的原因。此外,還會不斷檢查是否有相關(guān)的定時器超時,如果有則會跳轉(zhuǎn)到timers階段,然后執(zhí)行相應(yīng)的回調(diào)。
4 check
setImmediate是一個特殊的定時器方法,它占據(jù)了事件循環(huán)的一個階段,整個check階段就是為setImmediate方法而設(shè)置的。
當(dāng)事件循環(huán)到達(dá)poll階段后,就會檢查當(dāng)前代碼是否調(diào)用了setImmediate,但如果一個回調(diào)函數(shù)是被setImmediate方法調(diào)用的,事件循環(huán)就會跳出poll階段進(jìn)入check階段。
5 close
如果一個socket或者一個句柄被關(guān)閉,那么就會產(chǎn)生一個close事件,該事件回被加入到對應(yīng)的隊列中。close階段執(zhí)行完畢后,本輪事件循環(huán)結(jié)束,循環(huán)進(jìn)入到下一輪。
在Node中,事件隊列不止一個,定時器相關(guān)的事件和磁盤IO產(chǎn)生的事件需要不同的處理方式,如果把所有的事件都放在一個隊列里,勢必要增加許多類似switch/case的代碼;那樣的話倒不如將不同類型的事件歸類到不同的事件隊列里,然后一層層地遍歷下來,如果當(dāng)中出現(xiàn)了新的事件,就進(jìn)行相應(yīng)的處理。
Node.js
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。