Nodejs事件輪詢?cè)斀?/h1>

什么是事件輪詢

大家都知道咱台, JavaScript是單線程的络拌, 那么nodejs是如何做到非阻塞呢,在nodejs內(nèi)部使用了第三方庫(kù)libuv回溺,nodejs會(huì)把IO春贸,文件讀取等異步操作交由他處理,而nodejs主線程可以繼續(xù)去處理其他的事情遗遵。libuv會(huì)開(kāi)啟不同的線程去處理這些延時(shí)操作萍恕,處理完后,會(huì)把異步操作的回調(diào)函數(shù)放到nodejs的輪詢隊(duì)列中车要,nodejs會(huì)在適當(dāng)?shù)臅r(shí)候處理輪詢隊(duì)列中的回調(diào)函數(shù)允粤,從而實(shí)現(xiàn)非阻塞。所以,實(shí)際上nodejs在處理這些阻塞操作時(shí)类垫,并不是單線程的司光。

事件輪詢?cè)斀?/h2>

下圖是nodejs官網(wǎng)的事件輪詢流程圖

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

從圖中可以看出, 事件輪詢機(jī)制分為六個(gè)階段悉患,每個(gè)階段都有一個(gè) FIFO 隊(duì)列來(lái)執(zhí)行回調(diào)残家。雖然每個(gè)階段都是特殊的,但通常情況下售躁,當(dāng)事件循環(huán)進(jìn)入給定的階段時(shí)坞淮,它將執(zhí)行特定于該階段的任何操作,然后在該階段的隊(duì)列中執(zhí)行回調(diào)陪捷,直到隊(duì)列用盡或最大回調(diào)數(shù)已執(zhí)行回窘。當(dāng)該隊(duì)列已用盡或達(dá)到回調(diào)限制,事件循環(huán)將移動(dòng)到下一階段揩局。

事件輪詢階段詳解

timers階段

概述: timers階段用來(lái)處理setTimeout() 和 setInterval() 的回調(diào)函數(shù)毫玖。
詳解 :我們都使用過(guò)setTimeout(callback, delay), setInterval(callback, delay), 第一個(gè)參數(shù)是回調(diào)函數(shù)掀虎, 第二個(gè)參數(shù)是延遲時(shí)間(ms)凌盯,即多久之后執(zhí)行。但是在絕大多數(shù)情況下烹玉,它并不會(huì)絕對(duì)守時(shí)驰怎。因?yàn)椴僮飨到y(tǒng)調(diào)度或其它回調(diào)的運(yùn)行可能會(huì)延遲它們。比如說(shuō)二打, 當(dāng)輪詢進(jìn)入到poll階段的時(shí)候并且poll階段的回調(diào)隊(duì)列不為空县忌, 如果timers此時(shí)已經(jīng)有一個(gè)setTimeout達(dá)到了預(yù)設(shè)的延時(shí)時(shí)間, 系統(tǒng)也需要先處理完poll階段的回調(diào)隊(duì)列继效,才能去處理timers的回調(diào)隊(duì)列症杏。

pending callbacks階段

概述: 這個(gè)階段用來(lái)處理系統(tǒng)操作的回調(diào)函數(shù)
詳解 :此階段對(duì)某些系統(tǒng)操作(如 TCP 錯(cuò)誤類型)執(zhí)行回調(diào)。例如瑞信,如果 TCP 套接字在嘗試連接時(shí)接收到 ECONNREFUSED厉颤,這些錯(cuò)誤的回調(diào)將被放到此階段的回調(diào)函數(shù)。

idle prepare階段

概述: 此階段是僅供nodejs內(nèi)部操作調(diào)用凡简,我們不必討論

poll階段

概述: 這個(gè)階段主要用來(lái)處理如IO操作逼友,網(wǎng)絡(luò)請(qǐng)求等異步操作
詳解 :這各階段會(huì)有不同的情況
1.當(dāng)poll階段的回調(diào)函數(shù)隊(duì)列不為空的時(shí)候,則處理隊(duì)列中的回調(diào)函數(shù)秤涩,直到隊(duì)列為空或者達(dá)到系統(tǒng)處理的上限的時(shí)候帜乞,就跳過(guò)此階段,處理下一階段筐眷。
2.當(dāng)進(jìn)入poll階段的時(shí)候黎烈,如果此階段的回調(diào)隊(duì)列為空,系統(tǒng)會(huì)在此階段等待新的回調(diào)函數(shù)入隊(duì),再進(jìn)行處理照棋。如果一直等不到新的回調(diào)函數(shù)呢津畸?咋辦?阻塞在這里必怜?一直等肉拓?不會(huì)的,在這個(gè)階段會(huì)同時(shí)進(jìn)行檢測(cè)timers階段是否已經(jīng)有回調(diào)函數(shù)超時(shí)梳庆,如果有暖途,則馬上跳過(guò)poll階段,進(jìn)入下一個(gè)階段膏执。
那么在poll階段是如何利用libuv庫(kù)來(lái)處理io及文件讀取等操作的呢驻售?看下圖

libuv處理異步操作.png

從圖中可以看得出,libuv自身有一個(gè)EventQueue的隊(duì)列更米,這個(gè)隊(duì)列里面都是一些如文件讀取欺栗,網(wǎng)絡(luò)請(qǐng)求的操作,libuv依次去處理這個(gè)隊(duì)列中的操作征峦,libuv每當(dāng)從EventQueue中拿到一個(gè)處理事件迟几, 就會(huì)分配一個(gè)線程給它,讓這個(gè)線程去處理這個(gè)事件栏笆,當(dāng)這個(gè)線程處理完畢這個(gè)事件的時(shí)候类腮,就會(huì)將結(jié)果返回到EventQueue隊(duì)列, 當(dāng)再次獲取到該事件的時(shí)候蛉加, 發(fā)現(xiàn)已經(jīng)不是IO操作了蚜枢,就會(huì)把這個(gè)事件的回調(diào)函數(shù)放到poll階段的隊(duì)列中,再交由poll去處理回調(diào)函數(shù)针饥。所以EventQueue在每次出隊(duì)的時(shí)候都會(huì)進(jìn)行判斷該操作是否是IO操作厂抽,不是的話就直接返回給poll階段的隊(duì)列了。需要注意的是丁眼,libuv默認(rèn)只開(kāi)啟了4個(gè)線程筷凤,你可以通過(guò)設(shè)置環(huán)境變量來(lái)修改線程數(shù)量。

check階段

概述: 這個(gè)階段用來(lái)處理setImmediate的回調(diào)函數(shù)
詳解 :當(dāng)poll階段的回調(diào)隊(duì)列為空的時(shí)候(或者達(dá)到系統(tǒng)執(zhí)行的上限)户盯,就會(huì)進(jìn)入到check階段來(lái)處理setImmediate的回調(diào)函數(shù)嵌施。

close callbacks階段

概述: 這個(gè)階段用來(lái)處理如socket的close事件
詳解 :顧名思義, 關(guān)閉回調(diào)函數(shù)莽鸭, 如socket.on("close", () => {...})

process.nextTick及Promise

上面的討論一直未提及process.nextTick和Promise的執(zhí)行吗伤,原因是這兩個(gè)函數(shù)比較特殊,它們不由libuv去管理硫眨,而且它們的優(yōu)先級(jí)要高于事件輪詢的每一個(gè)階段足淆。它們會(huì)在事件輪詢的每一個(gè)階段之間執(zhí)行,注意是事件輪詢的每一個(gè)階段之間。process.nextTick會(huì)放在nextTickQueue巧号, Promise會(huì)放在microTaskQueue族奢,在每次事件輪詢進(jìn)入到下一個(gè)階段的時(shí)候, 都會(huì)檢查這兩個(gè)隊(duì)列是否為空丹鸿,不為空則馬上處理它們的回調(diào)越走。
注意因?yàn)閜rocess.nextTick會(huì)在事件輪詢每個(gè)階段之間執(zhí)行, 如果遞歸調(diào)用nextTick靠欢, 就會(huì)導(dǎo)致輪詢阻塞廊敌,所以盡量避免使用process.nextTick, 可以使用setImmediate代替门怪。

有趣的事情

從process.nextTick和setImmediate的名字上來(lái)看骡澈,setImmediate應(yīng)當(dāng)是要先于process.nextTick執(zhí)行的。但事實(shí)恰好相反掷空,nodejs官網(wǎng)也給出了解釋肋殴,這是nodejs歷史原因,很難再去修改他們的名字L沟堋护锤!??????

最后貼出整個(gè)事件輪詢的完整流程圖

屏幕快照 2019-09-08 下午2.43.06.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市减拭,隨后出現(xiàn)的幾起案子蔽豺,更是在濱河造成了極大的恐慌区丑,老刑警劉巖拧粪,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沧侥,居然都是意外死亡可霎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)宴杀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)癣朗,“玉大人,你說(shuō)我怎么就攤上這事旺罢】跤啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵扁达,是天一觀的道長(zhǎng)正卧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)跪解,這世上最難降的妖魔是什么炉旷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上窘行,老公的妹妹穿的比我還像新娘饥追。我一直安慰自己,他們只是感情好罐盔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布但绕。 她就那樣靜靜地躺著,像睡著了一般惶看。 火紅的嫁衣襯著肌膚如雪壁熄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天碳竟,我揣著相機(jī)與錄音草丧,去河邊找鬼。 笑死莹桅,一個(gè)胖子當(dāng)著我的面吹牛昌执,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诈泼,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼懂拾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铐达?” 一聲冷哼從身側(cè)響起岖赋,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓮孙,沒(méi)想到半個(gè)月后唐断,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杭抠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年脸甘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偏灿。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丹诀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出翁垂,到底是詐尸還是另有隱情铆遭,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布沿猜,位于F島的核電站枚荣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏邢疙。R本人自食惡果不足惜棍弄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一望薄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呼畸,春花似錦痕支、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至儒陨,卻和暖如春花嘶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹦漠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工椭员, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笛园。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓隘击,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親研铆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埋同,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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