溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Node.js中的eventloop怎么用

發(fā)布時(shí)間:2022-01-05 09:34:52 來(lái)源:億速云 閱讀:120 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要講解了“Node.js中的eventloop怎么用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Node.js中的eventloop怎么用”吧!

主線(xiàn)程從"任務(wù)隊(duì)列"中讀取事件,這個(gè)過(guò)程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱(chēng)為Event Loop(事件循環(huán))。

Node.js中的eventloop怎么用

其實(shí)在前面的文章我也講述過(guò)瀏覽器中的eventloop。然而在NodeJs中的eventloop與瀏覽器的是有區(qū)別的。對(duì)于寫(xiě)nodejs的人來(lái)說(shuō)掌握eventloop是一項(xiàng)很重要的技能。因?yàn)檫@意味著你不僅是會(huì)寫(xiě)js,而對(duì)NodeJs也是有研究的。

為什么要有eventloop?

我們知道NodeJs的本質(zhì)是把瀏覽器的v8搬到了操作系統(tǒng)中運(yùn)行,因此也把瀏覽器的事件循環(huán)拿過(guò)來(lái)了??墒菫槭裁磿?huì)出現(xiàn)eventloop這樣的設(shè)計(jì)呢?

從歷史原因上來(lái)看,js在設(shè)計(jì)時(shí)只是一門(mén)很簡(jiǎn)單的為了在頁(yè)面上操作一下dom的語(yǔ)言(相信大家都聽(tīng)過(guò)js只用了10天就設(shè)計(jì)出來(lái)的故事)。出于這個(gè)目標(biāo),我們當(dāng)然希望js的運(yùn)行盡可能的簡(jiǎn)單,輕量,有多輕呢?輕到j(luò)s的渲染引擎是在一個(gè)線(xiàn)程中運(yùn)行的。

那么問(wèn)題來(lái)了如果是在一個(gè)線(xiàn)程上運(yùn)行js,當(dāng)代碼是線(xiàn)性的時(shí)候,當(dāng)然是沒(méi)有問(wèn)題的。但在頁(yè)面上,我們需要用戶(hù)的交互,而這些交互是不知道為什么時(shí)候發(fā)生的。那js要怎么處理?如果眼前有正在運(yùn)行的代碼,一個(gè)用戶(hù)交互進(jìn)來(lái)之后,程序該怎么反應(yīng)?如果先處理用戶(hù)的交互,那原來(lái)的程序就會(huì)被暫停(也就是阻塞)。為了避免這種阻塞,js采用了一個(gè)辦法,就是用一個(gè)消息隊(duì)列,來(lái)存放這種用戶(hù)交互。等所有的程序跑完之后,再去消息隊(duì)列中拿交互事件,然后執(zhí)行。這樣就解決了阻塞的問(wèn)題了。

瀏覽器的eventloop

我們都知道瀏覽器在瀏覽頁(yè)面的時(shí)候,用戶(hù)交互是隨時(shí)可能發(fā)生的,為了可以即時(shí)響應(yīng)用戶(hù)。js是不會(huì)關(guān)閉的,他會(huì)不停的循環(huán)。大致如下:

向消息隊(duì)列拿任務(wù)-->執(zhí)行任務(wù)-->執(zhí)行完畢--> 向消息隊(duì)列拿任務(wù)--> ....

當(dāng)然我們?cè)谥暗氖录h(huán)文章中講過(guò),為了給不同的異步任務(wù)分類(lèi),在事件循環(huán)中其實(shí)是有宏任務(wù)和微任務(wù)的區(qū)分的。他們的執(zhí)行大致為

向消息隊(duì)列拿微任務(wù)-->執(zhí)行微任務(wù)-->微任務(wù)執(zhí)行完畢--> 向消息隊(duì)列拿宏任務(wù)-->執(zhí)行宏任務(wù)-->宏任務(wù)執(zhí)行完畢-->向消息隊(duì)列拿微任務(wù)-->...

NodeJs的eventloop

node的事件循環(huán)其實(shí)大致思路跟在瀏覽器上的是相似的,但nodeJs對(duì)不同的宏任務(wù)又作出了不同時(shí)期的區(qū)分。下面是官方的流程圖:

Node.js中的eventloop怎么用

可以看到nodeJs中每次事件循環(huán)被分成了具體的6個(gè)時(shí)期,每個(gè)時(shí)期會(huì)用指定的宏任務(wù)。然后在每個(gè)時(shí)期的宏任務(wù)執(zhí)行之前,會(huì)優(yōu)先執(zhí)行完微任務(wù)隊(duì)列。

總覽

timers執(zhí)行由setTimeout()setInterval() 觸發(fā)的回調(diào)
pending callbacks執(zhí)行延遲到下一個(gè)循環(huán)迭代的I / O回調(diào)
idle, prepare只在內(nèi)部使用,開(kāi)發(fā)者可以不關(guān)注
poll檢索新的I / O事件;執(zhí)行I / O相關(guān)的回調(diào)(會(huì)執(zhí)行幾乎所有的回調(diào),除了 close callbacks 以及 timers 調(diào)度的回調(diào)和 setImmediate() 調(diào)度的回調(diào),在恰當(dāng)?shù)臅r(shí)機(jī)將會(huì)阻塞在此階段)
check執(zhí)行setImmediate()
close callbacks比如socket.on('close', ...)

其實(shí)通過(guò)上述表格,我們已經(jīng)很清晰知道整個(gè)事件循環(huán)機(jī)制的執(zhí)行順序了。但可能大家還會(huì)有一些疑問(wèn)。下面來(lái)詳細(xì)講一下。

pending callbacks

這個(gè)階段其實(shí)是處理由于操作系統(tǒng)出錯(cuò),導(dǎo)致一些本應(yīng)在上次事件循環(huán)中執(zhí)行的回調(diào)。例如一些TCP錯(cuò)誤。因此這部分,開(kāi)發(fā)者不能主動(dòng)操作,是NodeJs的一些容錯(cuò)機(jī)制。

check

同樣的,setImmediate是nodejs特有的api,他可以立即創(chuàng)建一個(gè)異步宏任務(wù)。不僅如此,nodejs在事件循環(huán)中還專(zhuān)門(mén)設(shè)了一個(gè)check時(shí)期,在這個(gè)時(shí)期會(huì)專(zhuān)門(mén)執(zhí)行setImmediate的回調(diào)。甚至你可以在這個(gè)時(shí)期中如果不停的產(chǎn)生setImmediate回調(diào),eventloop會(huì)優(yōu)先處理。

close callbacks

這個(gè)時(shí)期處理關(guān)閉事件,如socket.on('close', ...) 等這樣可以確保在一些通訊結(jié)束前,所有任務(wù)都完成了。

微任務(wù)在eventloop中

我們先來(lái)回顧瀏覽器與nodejs的差異:

宏任務(wù):

任務(wù)瀏覽器Node
I/O??
setTimeout??
setInterval??
setImmediate??
requestAnimationFrame??

微任務(wù):

任務(wù)瀏覽器Node
process.nextTick??
MutationObserver??
Promise.then catch finally??

可以看到process.nextTick是nodejs特有的微任務(wù),不僅如此,process.nextTick()的優(yōu)先級(jí)高于所有的微任務(wù),每一次清空微任務(wù)列表的時(shí)候,都是先執(zhí)行 process.nextTick()

執(zhí)行差異

不僅是任務(wù)類(lèi)型上有差異,在執(zhí)行上2個(gè)環(huán)境其實(shí)也有差異。在瀏覽器上執(zhí)行任務(wù)的時(shí)候,每執(zhí)行一個(gè)宏任務(wù)之前,需要先確保微任務(wù)隊(duì)列執(zhí)行完了。而在nodejs上是每個(gè)時(shí)期之前,先確保微任務(wù)隊(duì)列執(zhí)行完。也就是說(shuō)在假如在timer時(shí)期,會(huì)先把所有setTimeout,setInterval的宏任務(wù)執(zhí)行完。在執(zhí)行完微任務(wù),再進(jìn)入下個(gè)時(shí)期。

注意:以上執(zhí)行規(guī)則是在nodejs的v11版本之前的規(guī)則。在11版本之后nodejs的執(zhí)行輸出是跟瀏覽器一樣的。

setImmediate() vs setTimeout()

setImmediate() 和 setTimeout()的執(zhí)行先后順序是不一定的,就是說(shuō)如果你不停地執(zhí)行以下代碼,每次得到的結(jié)果可能是不一樣的。

setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});

其中的原因是程序?qū)r(shí)間的處理是有誤差的。在setTimeout方法中設(shè)置的時(shí)間,不一定是準(zhǔn)確的。同時(shí)在回調(diào)觸發(fā)時(shí),也無(wú)法確認(rèn)事件循環(huán)處在哪個(gè)時(shí)期,可能是timer,也可能是check。所有會(huì)有不同的結(jié)果。

感謝各位的閱讀,以上就是“Node.js中的eventloop怎么用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Node.js中的eventloop怎么用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI