Node.js中的事件循環(huán),Timers和process.nextTick() 的探索之路

事件循環(huán)?

事件循環(huán)就是node.js去做一些非阻塞I/O操作笆包,那么問(wèn)題來(lái)了鲁冯,非阻塞操作又是什么呢?有一個(gè)事實(shí)對(duì)于js開發(fā)者都熟知的是色查,js是單線程的,也就是說(shuō)在一段時(shí)間內(nèi)只能夠處理一種任務(wù)撞芍,其他任務(wù)要執(zhí)行需要等待當(dāng)前任務(wù)執(zhí)行完之后再開始秧了。

由于大部分的現(xiàn)代內(nèi)核都是多線程的,它們能夠處理不同的操作執(zhí)行序无。當(dāng)其中的一個(gè)操作完成時(shí)验毡,內(nèi)核會(huì)告訴node,回調(diào)函數(shù)將會(huì)被加入到執(zhí)行隊(duì)列中等待被執(zhí)行帝嗡。

事件循環(huán)有好幾個(gè)階段晶通,每個(gè)階段都有先進(jìn)先出的回調(diào)隊(duì)列被執(zhí)行。每個(gè)階段都有它的特別之處哟玷,當(dāng)事件循環(huán)進(jìn)入被給予的階段時(shí)狮辽,它將會(huì)執(zhí)行確切的操作,直到上一隊(duì)列已經(jīng)執(zhí)行完接著執(zhí)行在該階段的回調(diào)巢寡,當(dāng)該階段上的所有隊(duì)列或回調(diào)都執(zhí)行完成之后喉脖,事件循環(huán)會(huì)轉(zhuǎn)向下一階段

?階段預(yù)覽

-?times:這個(gè)階段執(zhí)行把setTimeout()和setInterval()列入計(jì)劃的回調(diào)

1.?pending callbacks:執(zhí)行I/O回調(diào)

2.?idle, prepare: 僅為內(nèi)部使用

3.?poll: 獲取新的I/O事件;執(zhí)行I/O相關(guān)的回調(diào)(大部分關(guān)閉回調(diào)的異常)

4.?check: setImmediate()回調(diào)將會(huì)被喚起

5.?close callbacks: 一些關(guān)閉的回調(diào)抑月,例如socket.on('close', ...)

times

一個(gè)timer指定了一個(gè)閾值树叽,在一個(gè)回調(diào)被執(zhí)行之后而不是你想要讓它執(zhí)行的確定的時(shí)間。timer的回調(diào)在給定的確定時(shí)間之后將會(huì)盡可能早的執(zhí)行谦絮,然而题诵,操作系統(tǒng)或者其它回調(diào)的執(zhí)行可能會(huì)延遲timer的回調(diào)

?pending callbacks

這個(gè)階段執(zhí)行一些系統(tǒng)操作的回調(diào),例如tcp錯(cuò)誤的類型

poll

這個(gè)poll階段有兩個(gè)主要的函數(shù):

1.?計(jì)算它應(yīng)該阻塞的時(shí)間

2.?在該隊(duì)列中處理事件

當(dāng)事件循環(huán)進(jìn)入poll階段层皱,并且沒(méi)有timers被安排執(zhí)行性锭,以下之一將會(huì)發(fā)生:

1.?如果poll隊(duì)列不為空,事件循環(huán)將會(huì)同步迭代執(zhí)行它的調(diào)用隊(duì)列直到隊(duì)列已經(jīng)被執(zhí)行完成

2.?如果poll隊(duì)列是空的叫胖,以下之一將會(huì)發(fā)生:

????如果腳本中有setImmediate,事件循環(huán)將會(huì)停止poll階段篷店,繼續(xù)轉(zhuǎn)向check階段去執(zhí)行這些被安排的腳本

????若腳本中沒(méi)有setImmediate,事件循環(huán)將會(huì)等待被添加進(jìn)隊(duì)列的回調(diào),并馬上執(zhí)行它們

一旦poll隊(duì)列是空的,事件循環(huán)將會(huì)檢查那些已經(jīng)到達(dá)了設(shè)置的閾值的timers疲陕。若一個(gè)或更多的timers已經(jīng)準(zhǔn)備好方淤,事件循環(huán)將會(huì)轉(zhuǎn)向執(zhí)行timers階段,從而去執(zhí)行這些timers的回調(diào)函數(shù)

check

這個(gè)階段在poll階段已經(jīng)完成之后允許人馬上執(zhí)行回調(diào)蹄殃,如果poll階段變?yōu)榱碎e置狀態(tài)携茂,且腳本中有setImmediate,事件循環(huán)將會(huì)轉(zhuǎn)向check階段而不是等待。

setImmediate實(shí)際上是一個(gè)確定的timer诅岩,它運(yùn)行在一個(gè)事件循環(huán)的獨(dú)立的階段讳苦。它使用了一個(gè)libuv api,是在poll階段完成之后吩谦,執(zhí)行這些安排的回調(diào)

close callbacks

如果一個(gè)socket或者是處理突然的被關(guān)閉了鸳谜,這個(gè)close事件將會(huì)在這個(gè)階段觸發(fā)。另外式廷,它將會(huì)通過(guò) process.nextTick觸發(fā)咐扭。

setImmediate() VS setTimeout()?

他們很類似,但是表現(xiàn)卻不同滑废,這取決于他們何時(shí)被調(diào)用

1.?setImmediate()被設(shè)計(jì)為一旦當(dāng)前的poll階段完成時(shí)蝗肪,執(zhí)行回調(diào)

2.?setTimeout()是要在設(shè)置一個(gè)最小閾值的時(shí)間之后執(zhí)行回調(diào)

他們?cè)诒徽{(diào)用的不同環(huán)境下執(zhí)行順序會(huì)有所不同,然而蠕趁,若在I/O循環(huán)之內(nèi)薛闪,immediate的回調(diào)總是先執(zhí)行


????const fs = require('fs');

? ? fs.readFile(__filename, () ={

? ? ?setImmediate(() ={

? ? ?console.log('immediate');

??????});

??????setTimeout(() ={

????console.log('timeout');

??????}, 0);

????});

process.nextTick()?

盡管是異步api的一部分,但不是事件循環(huán)的部分俺陋。這個(gè)nextTickQueue將會(huì)在當(dāng)前操作完成之后被處理豁延,不管當(dāng)前事件循環(huán)處在哪一階段

setImmediate() VS process.nextTick()

1.?在同一階段process.nextTick()將會(huì)立即觸發(fā)執(zhí)行

2.?setImmediate()在下一次迭代循環(huán)中觸發(fā)

為什么使用process.nextTick()?

1.?允許用戶處理錯(cuò)誤,在事件循環(huán)繼續(xù)之前清除不需要的資源或者是再次發(fā)出請(qǐng)求

2.?有時(shí)候需要在執(zhí)行棧結(jié)束時(shí)但要在事件循環(huán)繼續(xù)之前執(zhí)行該回調(diào)函數(shù)

舉個(gè)栗子腊状,如果要在函數(shù)構(gòu)造器中觸發(fā)一個(gè)事件术浪,若按照以下寫法,是不會(huì)被調(diào)用執(zhí)行的寿酌,因?yàn)樵摯a并沒(méi)有被處理胰苏,構(gòu)造函數(shù)還沒(méi)有被執(zhí)行完成。


????const EventEmitter = require('events');

????const util = require('util');

? ? function MyEmitter() {

??????EventEmitter.call(this);

??????this.emit('event'); // 該事件觸發(fā)無(wú)效

????}

????util.inherits(MyEmitter, EventEmitter);

? ?const myEmitter = new MyEmitter();

????myEmitter.on('event', () => {

??????console.log('an event occurred!');

????});


若使用了process.nextTick(),就可以在構(gòu)造器執(zhí)行完成之后觸發(fā)該事件醇疼,從而觸發(fā)回調(diào)


????const EventEmitter = require('events');

????const util = require('util');

? ?function MyEmitter() {

??????EventEmitter.call(this);

? ? ?// use nextTick to emit the event once a handler is assigned

??????process.nextTick(() => {

????this.emit('event');

??????});

????}

util.inherits(MyEmitter, EventEmitter);

?const myEmitter = new MyEmitter();

????myEmitter.on('event', () => {

??????console.log('an event occurred!');

????});

參考資料:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硕并,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秧荆,更是在濱河造成了極大的恐慌倔毙,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碎绎,警方通過(guò)查閱死者的電腦和手機(jī)嫁怀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)购笆,“玉大人赖晶,你說(shuō)我怎么就攤上這事俄占∷呷澹” “怎么了葡缰?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)忱反。 經(jīng)常有香客問(wèn)我泛释,道長(zhǎng),這世上最難降的妖魔是什么温算? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任怜校,我火速辦了婚禮,結(jié)果婚禮上注竿,老公的妹妹穿的比我還像新娘茄茁。我一直安慰自己,他們只是感情好蔓搞,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著随橘,像睡著了一般喂分。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上机蔗,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天蒲祈,我揣著相機(jī)與錄音,去河邊找鬼萝嘁。 笑死梆掸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的牙言。 我是一名探鬼主播酸钦,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咱枉!你這毒婦竟也來(lái)了卑硫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚕断,失蹤者是張志新(化名)和其女友劉穎欢伏,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亿乳,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硝拧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片障陶。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡滋恬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咸这,到底是詐尸還是另有隱情夷恍,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布媳维,位于F島的核電站酿雪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侄刽。R本人自食惡果不足惜指黎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望州丹。 院中可真熱鬧醋安,春花似錦、人聲如沸墓毒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)所计。三九已至柠辞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間主胧,已是汗流浹背叭首。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踪栋,地道東北人焙格。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像夷都,于是被迫代替她去往敵國(guó)和親眷唉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

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