主執行緒從"任務佇列"讀取事件,這個過程是循環不斷的,所以整個的這種運作機制又稱為Event Loop(事件循環)。以下這篇文章就來帶大家掌握Node.js中的eventloop,希望對大家有幫助!
其實在前面的文章我也講述過瀏覽器中的eventloop。然而在NodeJs中的eventloop與瀏覽器的卻是有差別的。對於寫nodejs的人來說掌握eventloop是一項很重要的技能。因為這代表你不只是會寫js,而對NodeJs也是有研究的。
我們知道NodeJs的本質是把瀏覽器的v8搬到了作業系統中運行,因此也把瀏覽器的事件循環拿過來了。可是為什麼會出現eventloop這樣的設計呢?
從歷史原因來看,js在設計時只是一門很簡單的為了在頁面上操作一下dom的語言(相信大家都聽過js只花了10天就設計出來的故事)。基於這個目標,我們當然希望js的運作盡可能的簡單,輕量,有多輕呢?輕到js的渲染引擎是在一個執行緒中運行的。
那麼問題來瞭如果是在一個執行緒上執行js,當程式碼是線性的時候,當然是沒有問題的。但在頁面上,我們需要使用者的交互,而這些交互是不知道為什麼時候會發生的。那js要怎麼處理?如果眼前有正在運作的程式碼,一個使用者互動進來之後,程式該怎麼反應?如果先處理使用者的交互,那原來的程式就會被暫停(也就是阻塞)。為了避免這種阻塞,js採用了一個辦法,就是用一個訊息佇列,來存放這種使用者互動。等所有的程式跑完之後,再去訊息佇列中拿互動事件,然後執行。這樣就解決了阻塞的問題了。
我們都知道瀏覽器在瀏覽頁面的時候,使用者互動是隨時可能發生的,為了可以即時回應使用者。 js是不會關閉的,他會不停的循環。大致如下:
向消息队列拿任务-->执行任务-->执行完毕--> 向消息队列拿任务--> ....
當然我們在之前的事件循環文章中講過,為了給不同的非同步任務分類,在事件循環中其實是有宏任務和微任務的區分的。他們的執行大致為
向消息队列拿微任务-->执行微任务-->微任务执行完毕--> 向消息队列拿宏任务-->执行宏任务-->宏任务执行完毕-->向消息队列拿微任务-->...
#node的事件循環其實大致思路跟在瀏覽器上的是相似的,但nodeJs對不同的宏任務又作出了不同時期的區分。下面是官方的流程圖:
可以看到nodeJs中每次事件循環被分成了具體的6個時期,每個時期會用指定的巨集任務。然後在每個時期的巨集任務執行之前,會優先執行完微任務佇列。
#timers | 執行由setTimeout() 和setInterval() 觸發的回呼 | |
---|---|---|
#pending callbacks | ##執行延遲到下一個循環迭代的I / O回呼||
idle, prepare | 只在內部使用,開發者可以不關注||
poll | 檢索新的I / O事件;執行I / O相關的回呼(會執行幾乎所有的回調,除了close callbacks 以及timers 調度的回調和setImmediate() 調度的回調,在適當的時機將會阻塞在此階段)||
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() 和 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中文網其他相關文章!