帶你了解NodeJS事件循環(huán)
瀏覽器中存在兩個任務隊列,一個是宏任務一個是微任務。但是在NodeJS中一共存在六個事件隊列,timers
,pending callbacks,idle prepare
,poll,check
,close callbacks
。每一個隊列里面存放的都是回調函數(shù)callback
。
這六個隊列是按順序執(zhí)行的。每個隊列負責存儲不同的任務。
timer
里面存在的是setTimeout與setInterval的回調函數(shù)pending callback
是執(zhí)行操作系統(tǒng)的回調,例如tcp,udp。idle
和prepare
只在系統(tǒng)內部進行使用。一般開發(fā)者用不到poll
執(zhí)行與IO相關的回調操作check
中存放setImmediate中的回調。close callbacks
執(zhí)行close事件的回調。
在Node中代碼從上到下同步執(zhí)行,在執(zhí)行過程中會將不同的任務添加到相應的隊列中,比如說setTimeout就會放在timers中, 如果遇到文件讀寫就放在poll里面,等到整個同步代碼執(zhí)行完畢之后就會去執(zhí)行滿足條件的微任務。可以假想有一個隊列用于存放微任務,這個隊列和前面的六種沒有任何關系。
當同步代碼執(zhí)行完成之后會去執(zhí)行滿足條件的微任務,一旦所有的微任務執(zhí)行完畢就會按照上面列出的順序去執(zhí)行隊列當中滿足條件的宏任務。
首先會執(zhí)行timers當中滿足條件的宏任務,當他將timers中滿足的任務執(zhí)行完成之后就會去執(zhí)行隊列的切換,在切換之前會先去清空微任務列表中的微任務。
所以微任務執(zhí)行是有兩個時機的,第一個時機是所有的同步代碼執(zhí)行完畢,第二個時機隊列切換前。
注意在微任務中nextTick的執(zhí)行優(yōu)先級要高于Promise
,這個只能死記了。
setTimeout(() => { ? ? console.log('s1'); }) Promise.resolve().then(() => { ? ? console.log('p1'); }) console.log('start'); process.nextTick(() => { ? ? console.log('tick'); }) setImmediate(() => { ? ? console.log('st'); }) console.log('end'); // start end tick p1 s1 st
setTimeout(() => { ? ? console.log('s1'); ? ? Promise.resolve().then(() => { ? ? ? ? console.log('p1'); ? ? }) ? ? process.nextTick(() => { ? ? ? ? console.log('t1'); ? ? }) }) Promise.resolve().then(() => { ? ? console.log('p2') }) console.log('start'); setTimeout(() => { ? ? console.log('s2'); ? ? Promise.resolve().then(() => { ? ? ? ? console.log('p3'); ? ? }) ? ? process.nextTick(() => { ? ? ? ? console.log('t2'); ? ? }) }) console.log('end'); // start end p2 s1 s2 t1 t2 p1 p3
Node
與瀏覽器事件環(huán)執(zhí)行是有一些不同的。
首先任務隊列數(shù)不同,瀏覽器一般只有宏任務和微任務兩個隊列,而Node中除了微任務隊列外還有6個事件隊列。
其次微任務執(zhí)行時機不同,不過他們也有相同的地方就是在同步任務執(zhí)行完畢之后都會去看一下微任務是否存在可執(zhí)行的。對瀏覽器來說每當一個宏任務執(zhí)行完成之后就會清空一次微任務隊列。在Node中只有在事件隊列切換時才會去清空微任務隊列。
最后在Node平臺下微任務執(zhí)行是有優(yōu)先級的,nextTick優(yōu)先于Promise.then
, 而瀏覽器中則是先進先出。
setTimeout(() => { ? ? console.log('timeout'); }) setImmediate(() => { ? ? console.log('immdieate'); })
在Node中時而會先輸出timeout
時而會先輸出immdieate
,這是因為setTimeout
是需要接收一個時間參數(shù)的,如果沒寫就是一個0,我們都知道無論是在Node還是在瀏覽器,程序是不可能真的是0,他會受很多的因素影響。這取決于運行的環(huán)境。
如果setTimeout先執(zhí)行就會放在timers隊列中,這樣timeout就會先輸入,如果setTimeout因為某些原因后執(zhí)行了,那么check隊列中的immdieate
就會先執(zhí)行。這就是為什么時而輸出timeout時而輸出immdieate
。
const fs = require('fs'); fs.readFile('./a.txt', () => { ? ? setTimeout(() => { ? ? ? ? console.log('timeout'); ? ? }, 0) ? ? setImmediate(() => { ? ? ? ? console.log('immdieate'); ? ? }) })
這種情況就會一直先輸出immdieate
后輸出timeout
,這是因為,代碼執(zhí)行的時候會在timers里面加入timeout, 在poll中加入fs的回調,在check中加入immdieate
。fs的回調執(zhí)行結束之后實在poll隊列,隊列切換的時候首先會去看微任務,但是這里沒有微任務就會繼續(xù)向下,下面就是check隊列而不是timers隊列,所以poll清空之后會切換到check隊列,執(zhí)行immdieate回調。
到此這篇關于帶你了解NodeJS事件循環(huán)的文章就介紹到這了,更多相關NodeJS事件循環(huán)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在Linux系統(tǒng)上更新Node.js到最新版本的3種方法小結
這篇文章主要介紹了在Linux系統(tǒng)上更新Node.js到最新版本的3種方法,使用NVM,使用NPM,用二進制包更新Node.js,文中有詳解更新方法,需要的朋友可以參考下2023-09-09Node.js操作Firebird數(shù)據(jù)庫教程
這篇文章主要為大家分享了Node.js操作Firebird數(shù)據(jù)庫教程,思路清晰便于大家理解,感興趣的小伙伴們可以參考一下2016-03-03