首頁  >  文章  >  web前端  >  一文帶你了解Node.js中的eventloop

一文帶你了解Node.js中的eventloop

青灯夜游
青灯夜游轉載
2022-01-04 18:44:132299瀏覽

主執行緒從"任務佇列"讀取事件,這個過程是循環不斷的,所以整個的這種運作機制又稱為Event Loop(事件循環)。以下這篇文章就來帶大家掌握Node.js中的eventloop,希望對大家有幫助!

一文帶你了解Node.js中的eventloop

其實在前面的文章我也講述過瀏覽器中的eventloop。然而在NodeJs中的eventloop與瀏覽器的卻是有差別的。對於寫nodejs的人來說掌握eventloop是一項很重要的技能。因為這代表你不只是會寫js,而對NodeJs也是有研究的。

為什麼要有eventloop?

我們知道NodeJs的本質是把瀏覽器的v8搬到了作業系統中運行,因此也把瀏覽器的事件循環拿過來了。可是為什麼會出現eventloop這樣的設計呢?

從歷史原因來看,js在設計時只是一門很簡單的為了在頁面上操作一下dom的語言(相信大家都聽過js只花了10天就設計出來的故事)。基於這個目標,我們當然希望js的運作盡可能的簡單,輕量,有多輕呢?輕到js的渲染引擎是在一個執行緒中運行的。

那麼問題來瞭如果是在一個執行緒上執行js,當程式碼是線性的時候,當然是沒有問題的。但在頁面上,我們需要使用者的交互,而這些交互是不知道為什麼時候會發生的。那js要怎麼處理?如果眼前有正在運作的程式碼,一個使用者互動進來之後,程式該怎麼反應?如果先處理使用者的交互,那原來的程式就會被暫停(也就是阻塞)。為了避免這種阻塞,js採用了一個辦法,就是用一個訊息佇列,來存放這種使用者互動。等所有的程式跑完之後,再去訊息佇列中拿互動事件,然後執行。這樣就解決了阻塞的問題了。

瀏覽器的eventloop

我們都知道瀏覽器在瀏覽頁面的時候,使用者互動是隨時可能發生的,為了可以即時回應使用者。 js是不會關閉的,他會不停的循環。大致如下:

向消息队列拿任务-->执行任务-->执行完毕--> 向消息队列拿任务--> ....

當然我們在之前的事件循環文章中講過,為了給不同的非同步任務分類,在事件循環中其實是有宏任務和微任務的區分的。他們的執行大致為

向消息队列拿微任务-->执行微任务-->微任务执行完毕--> 向消息队列拿宏任务-->执行宏任务-->宏任务执行完毕-->向消息队列拿微任务-->...

NodeJs的eventloop

#node的事件循環其實大致思路跟在瀏覽器上的是相似的,但nodeJs對不同的宏任務又作出了不同時期的區分。下面是官方的流程圖:

一文帶你了解Node.js中的eventloop

可以看到nodeJs中每次事件循環被分成了具體的6個時期,每個時期會用指定的巨集任務。然後在每個時期的巨集任務執行之前,會優先執行完微任務佇列。

總覽

##執行延遲到下一個循環迭代的I / O回呼只在內部使用,開發者可以不關注檢索新的I / O事件;執行I / O相關的回呼(會執行幾乎所有的回調,除了close callbacks 以及timers 調度的回調和setImmediate() 調度的回調,在適當的時機將會阻塞在此階段)執行例如

其实通过上述表格,我们已经很清晰知道整个事件循环机制的执行顺序了。但可能大家还会有一些疑问。下面来详细讲一下。

pending callbacks

这个阶段其实是处理由于操作系统出错,导致一些本应在上次事件循环中执行的回调。例如一些TCP错误。因此这部分,开发者不能主动操作,是NodeJs的一些容错机制。

check

同样的,setImmediate是nodejs特有的api,他可以立即创建一个异步宏任务。不仅如此,nodejs在事件循环中还专门设了一个check时期,在这个时期会专门执行setImmediate的回调。甚至你可以在这个时期中如果不停的产生setImmediate回调,eventloop会优先处理。

close callbacks

这个时期处理关闭事件,如socket.on('close', ...) 等这样可以确保在一些通讯结束前,所有任务都完成了。

微任务在eventloop中

我们先来回顾浏览器与nodejs的差异:

宏任务:

#timers 執行由setTimeout()setInterval() 觸發的回呼
#pending callbacks
idle, prepare
poll
check setImmediate()
close callbacks socket.on('close', ...)
任务 浏览器 Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

微任务:

任务 浏览器 Node
process.nextTick
MutationObserver
Promise.then catch finally

可以看到process.nextTick是nodejs特有的微任务,不仅如此,process.nextTick()的优先级高于所有的微任务,每一次清空微任务列表的时候,都是先执行 process.nextTick()

执行差异

不仅是任务类型上有差异,在执行上2个环境其实也有差异。在浏览器上执行任务的时候,每执行一个宏任务之前,需要先确保微任务队列执行完了。而在nodejs上是每个时期之前,先确保微任务队列执行完。也就是说在假如在timer时期,会先把所有setTimeout,setInterval的宏任务执行完。在执行完微任务,再进入下个时期。

注意:以上执行规则是在nodejs的v11版本之前的规则。在11版本之后nodejs的执行输出是跟浏览器一样的。

setImmediate() vs setTimeout()

setImmediate() 和 setTimeout()的执行先后顺序是不一定的,就是说如果你不停地执行以下代码,每次得到的结果可能是不一样的。

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

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

其中的原因是程序对时间的处理是有误差的。在setTimeout方法中设置的时间,不一定是准确的。同时在回调触发时,也无法确认事件循环处在哪个时期,可能是timer,也可能是check。所有会有不同的结果。

总结

eventloop是js运行机制里的重点内容,对于NodeJs来说,eventloop的操作空间则更大。因为它被细分为不同的时期,从而让我们可能把逻辑进一步细化。同时利用nextTick的最高优先级,可以写出在浏览器无法实现的代码。因此对于深入NodeJs的开发者来说,eventloop往往是他们考察新人对NodeJs理解的第一步。

更多node相关知识,请访问:nodejs 教程!!

以上是一文帶你了解Node.js中的eventloop的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除