從官方說明上解析Nodejs 事件循環(huán)

前言

好久沒寫博客了,因?yàn)橼s著學(xué)業(yè)和打碼..........鴿了好久抱歉抱歉orz
如果你比較有能力,我完全推薦你直接去看官方文檔
https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/
這里是在官方文檔的基礎(chǔ)上做的一個自己的解析與理解。

正文

事件循環(huán)操作順序:

 ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

本文著重介紹timers(定時器) poll(輪詢) check()三個階段

part 1.定時器階段(timers)

取自官方:

計時器指定 可以執(zhí)行所提供回調(diào)閾值,而不是用戶希望其執(zhí)行的確切時間疫稿。在指定的一段時間間隔后谆刨, 計時器回調(diào)將被盡可能早地運(yùn)
行渺鹦。但是舀透,操作系統(tǒng)調(diào)度或其它正在運(yùn)行的回調(diào)可能會延遲它們恶耽。

注意輪詢 階段 控制何時定時器執(zhí)行密任。

例如,假設(shè)您調(diào)度了一個在 100 毫秒后超時的定時器偷俭,然后您的腳本開始異步讀取會耗費(fèi) 95 毫秒的文件:

const fs = require('fs');

function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
 fs.readFile('/path/to/file', callback);
}

const timeoutScheduled = Date.now();

setTimeout(() => {
 const delay = Date.now() - timeoutScheduled;

 console.log(`${delay}ms have passed since I was scheduled`);
}, 100);

// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {
const startCallback = Date.now();

 // do something that will take 10ms...
 while (Date.now() - startCallback < 10) {
   // do nothing
 }
});

當(dāng)事件循環(huán)進(jìn)入 輪詢 階段時浪讳,它有一個空隊列(此時 fs.readFile() 尚未完成),因此它將等待剩下的毫秒數(shù)涌萤,直到達(dá)到最快的一個計時器閾值為止淹遵。當(dāng)它等待 95 毫秒過后時,fs.readFile() 完成讀取文件形葬,它的那個需要 10 毫秒才能完成的回調(diào)合呐,將被添加到 輪詢 隊列中并執(zhí)>行。當(dāng)回調(diào)完成時笙以,隊列中不再有回調(diào),因此事件循環(huán)機(jī)制將查看最快到達(dá)閾值的計時器冻辩,然后將回到 計時器 階段猖腕,以執(zhí)行定時器的回調(diào)。在本示例中恨闪,您將看到調(diào)度計時器到它的回調(diào)被執(zhí)行之間的總延遲將為 105 毫秒倘感。
注意:為了防止 輪詢 階段餓死事件循環(huán),libuv(實(shí)現(xiàn) Node.js 事件循環(huán)和平臺的所有異步行為的 C 函數(shù)庫)咙咽,在停止輪詢以獲得更多事件之前老玛,還有一個硬性最大值(依賴于系統(tǒng))。

注意到這句話:

當(dāng)事件循環(huán)進(jìn)入 輪詢 階段時,它有一個空隊列(此時 fs.readFile() 尚未完成)蜡豹,因此它將等待剩下的毫秒數(shù)麸粮,直到達(dá)到最快的一個計時器閾值為止。

意味著在輪詢階段有一個阻塞的過程(同步等待意味著阻塞)
這個阻塞并非一成不變,會在輪詢隊列由 空 - >回調(diào)入隊(這里是fs.readFile()) 時清空隊列,執(zhí)行回調(diào),此時可能會延遲即將到來的timer函數(shù)

大致過程->

[ ? ??????? ? ??????? ? ??????? ] 0ms
->計算阻塞時間
->等待
[ fs.readFileCallback ] 95ms 這是一個意外的事件(可能不會發(fā)生)

->readFileCallback to end 105ms
->檢測到可以執(zhí)行time回調(diào)了 雖然已經(jīng)延遲5ms
->回到timer階段
->執(zhí)行time回調(diào)

略過了pending callback階段,因?yàn)椴挥绊憣κ录犃械睦斫?/strong>

part 2.輪詢階段(poll)

輪詢 階段有兩個重要的功能:

  1. 計算應(yīng)該阻塞和輪詢 I/O 的時間镜廉。[1]

  2. 然后弄诲,處理 輪詢 隊列里的事件。
    當(dāng)事件循環(huán)進(jìn)入 輪詢 階段且 沒有被調(diào)度的計時器時 娇唯,將發(fā)生以下兩種情況之一:

如果 輪詢 隊列 不是空的 齐遵,事件循環(huán)將循環(huán)訪問回調(diào)隊列并同步執(zhí)行它們,直到隊列已用盡塔插,或者達(dá)到了與系統(tǒng)相關(guān)的硬性限制梗摇。[2]

如果 輪詢 隊列 是空的 ,還有兩件事發(fā)生:

如果腳本被 setImmediate() 調(diào)度想许,則事件循環(huán)將結(jié)束 輪詢 階段留美,并繼續(xù) 檢查 階段以執(zhí)行那些被調(diào)度的腳本。

如果腳本 未被 setImmediate()調(diào)度伸刃,則事件循環(huán)將等待回調(diào)被添加到隊列中谎砾,然后立即執(zhí)行。

一旦 輪詢 隊列為空捧颅,事件循環(huán)將檢查 已達(dá)到時間閾值的計時器景图。如果一個或多個計時器已準(zhǔn)備就緒,則事件循環(huán)將繞回計時器階段以執(zhí)行這些計時器的回調(diào)碉哑。

檢查階段
此階段允許人員在輪詢階段完成后立即執(zhí)行回調(diào)挚币。如果輪詢階段變?yōu)榭臻e狀態(tài),并且腳本使用 setImmediate() 后被排列在隊列中扣典,則事件循環(huán)可能繼續(xù)到 檢查 階段而不是等待妆毕。

setImmediate() 實(shí)際上是一個在事件循環(huán)的單獨(dú)階段運(yùn)行的特殊計時器。它使用一個 libuv API 來安排回調(diào)在 輪詢 階段完成后執(zhí)行贮尖。

通常笛粘,在執(zhí)行代碼時,事件循環(huán)最終會命中輪詢階段湿硝,在那等待傳入連接薪前、請求等。但是关斜,如果回調(diào)已使用 setImmediate()調(diào)度過示括,并且輪詢階段變?yōu)榭臻e狀態(tài),則它將結(jié)束此階段痢畜,并繼續(xù)到檢查階段而不是繼續(xù)等待輪詢事件垛膝。

注釋[1]猜測和timer階段的硬性最大值有關(guān)鳍侣,此時要算出本次輪詢階段一共花費(fèi)多長時間,總不能一直卡著吧!
注釋[2]這里的輪詢隊列說的應(yīng)該就是事件隊列,不過除開官方文章,很多描述事件循環(huán)的文章喜歡把它叫做事件隊列吼拥,這是不是一種長期的誤解倚聚?

個人簡述此階段:
[1]計算輪詢時間T1
[2]處理輪詢隊列事件
[3]判斷輪詢隊列情況
不空->盡可能在T1內(nèi)執(zhí)行隊列中的事件
空->等待回調(diào)被添加中,然后立即執(zhí)行
空->(特殊情況)在此期間,如果被setImmediate調(diào)度,則直接進(jìn)入下一階段扔罪。

part 3.檢查階段(check)

此階段允許人員[1]在輪詢階段完成后立即執(zhí)行回調(diào)秉沼。如果輪詢階段變?yōu)榭臻e狀態(tài)矿酵,并且腳本使用 setImmediate() 后被排列在隊列中,則事件循環(huán)可能繼續(xù)到檢查階段而不是等待全肮。

setImmediate() 實(shí)際上是一個在事件循環(huán)的單獨(dú)階段運(yùn)行的特殊計時器敞咧。它使用一個 libuv API 來安排回調(diào)在 輪詢 階段完成后執(zhí)行。

通常辜腺,在執(zhí)行代碼時,事件循環(huán)最終會命中輪詢階段评疗,在那等待傳入連接、請求等百匆。但是砌些,如果回調(diào)已使用 setImmediate()調(diào)度過,并且輪詢階段變?yōu)榭臻e狀態(tài)加匈,則它將結(jié)束此階段存璃,并繼續(xù)到檢查階段而不是繼續(xù)等待輪詢事件雕拼。[2]

[1]人員?說實(shí)話這里不是很懂人員的含義是指程序員嗎?但查看英文文檔,這里的人員就是people.有些疑惑
[2]這里指的就是上文注解中:空->(特殊情況)在此期間,如果被setImmediate調(diào)度,則直接進(jìn)入下一階段。
說實(shí)話這里的check階段是專門為 setImmediate準(zhǔn)備的,setImmediate為何有這么大能耐啥寇?

part 4.關(guān)閉的回調(diào)函數(shù)階段(close callbacks)

如果套接字或處理函數(shù)突然關(guān)閉(例如 socket.destroy()),則'close' 事件將在這個階段發(fā)出示姿。否則它將通過 process.nextTick() 發(fā)出。

當(dāng)此階段結(jié)束栈戳,node將回到timers進(jìn)行循環(huán)难裆,也就是完成了一輪事件循環(huán)镊掖、

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褂痰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子缩歪,更是在濱河造成了極大的恐慌,老刑警劉巖匪蝙,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異千元,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幸海,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門奥务,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人氯葬,你說我怎么就攤上這事∫绨” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵阀参,是天一觀的道長瞻坝。 經(jīng)常有香客問我蛛壳,道長所刀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任浮创,我火速辦了婚禮忧吟,結(jié)果婚禮上斩披,老公的妹妹穿的比我還像新娘讹俊。我一直安慰自己煌抒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布寡壮。 她就那樣靜靜地躺著,像睡著了一般况既。 火紅的嫁衣襯著肌膚如雪这溅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天坏挠,我揣著相機(jī)與錄音,去河邊找鬼降狠。 笑死,一個胖子當(dāng)著我的面吹牛榜配,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播临燃,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼烙心,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淫茵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤铆铆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后薄货,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碍论,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年赊瞬,在試婚紗的時候發(fā)現(xiàn)自己被綠了先煎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巧涧。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡遥倦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缩筛,到底是詐尸還是另有隱情,我是刑警寧澤瞎抛,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布却紧,位于F島的核電站,受9級特大地震影響晓殊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巫俺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一介汹、第九天 我趴在偏房一處隱蔽的房頂上張望却嗡。 院中可真熱鬧嘹承,春花似錦、人聲如沸赶撰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘤载。三九已至否灾,卻和暖如春鸣奔,著一層夾襖步出監(jiān)牢的瞬間惩阶,已是汗流浹背扣汪。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崭别,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓舞痰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親响牛。 傳聞我的和親對象是個殘疾皇子赫段,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容