示例代碼
瀏覽器和 Node 都有事件輪詢的機(jī)制贩幻,雖然都屬于 JavaScript轿腺,但二者的內(nèi)部機(jī)制完全不同。
以下面這段代碼為例
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
Promise.resolve().then(function() {
console.log('promise3')
})
在瀏覽器中它的輸出順序是 promise3=>timer1=>promise1=>timer2=>promise2丛楚。
而在 Node 中它的輸出順序變?yōu)榱?promise3=>timer1=>timer2=>promise1=>promise2族壳。
宏任務(wù)的分類與微任務(wù)何時(shí)執(zhí)行
接下來(lái)我們先說明兩個(gè)概念——宏任務(wù)與微任務(wù):
- macrotask:包含執(zhí)行整體的js代碼,事件回調(diào)趣些,XHR回調(diào)仿荆,定時(shí)器(setTimeout/setInterval/setImmediate),IO操作坏平,UI render
- microtask:更新應(yīng)用程序狀態(tài)的任務(wù)拢操,包括promise回調(diào),MutationObserver功茴,process.nextTick庐冯,Object.observe
瀏覽器與 Node 事件輪詢的不同點(diǎn)就在于宏任務(wù)是否歸類與微任務(wù)何時(shí)執(zhí)行。
在瀏覽器中坎穿,宏任務(wù)會(huì)按照事件隊(duì)列中的順序依次執(zhí)行,宏任務(wù)有可能產(chǎn)生微任務(wù)返劲,微任務(wù)隊(duì)列會(huì)在當(dāng)前宏任務(wù)執(zhí)行結(jié)束后立即執(zhí)行玲昧。
在 Node 中,將宏任務(wù)分為六種篮绿,如果加上整體的 js 代碼一共有七種孵延。具體如下:
timers 階段:這個(gè)階段執(zhí)行timer(setTimeout、setInterval)的回調(diào)
I/O callbacks 階段:執(zhí)行一些系統(tǒng)調(diào)用錯(cuò)誤亲配,比如網(wǎng)絡(luò)通信的錯(cuò)誤回調(diào)
idle, prepare 階段:僅node內(nèi)部使用
poll 階段:獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里
check 階段:執(zhí)行 setImmediate() 的回調(diào)
close callbacks 階段:執(zhí)行 socket 的 close 事件回調(diào)
在 Node 中 js 整體代碼執(zhí)行結(jié)束后尘应,會(huì)將相應(yīng)的宏任務(wù)放到相應(yīng)的階段,然后從 timer 階段開始不斷循環(huán)執(zhí)行吼虎。每個(gè)階段產(chǎn)生的宏任務(wù)會(huì)放到其應(yīng)該屬于的階段犬钢,產(chǎn)生的微任務(wù)隊(duì)列會(huì)在當(dāng)前階段執(zhí)行結(jié)束后立即執(zhí)行。
代碼分析
下面我們來(lái)逐步分析兩個(gè)環(huán)境的輸出為什么不同思灰。
首先可以確定一點(diǎn)玷犹,整體 js 代碼會(huì)先執(zhí)行,并且從上到下同步執(zhí)行一遍洒疚。在執(zhí)行的過程中歹颓,會(huì)碰到異步的代碼,即 Promise.then 和 setTimeout 油湖,這兩個(gè)函數(shù)內(nèi)部的回調(diào)函數(shù)都不會(huì)在這個(gè)同步過程中執(zhí)行巍扛。
整體的 js 代碼執(zhí)行后產(chǎn)生了一個(gè)微任務(wù) (promise3) 和兩個(gè)宏任務(wù)(timer1、timer2)乏德。
在瀏覽器中會(huì)先執(zhí)行 promise3 再執(zhí)行 timer1撤奸,執(zhí)行 timer1 后產(chǎn)生的 promise1 會(huì)緊接著 timer1 執(zhí)行,最后是 timer2 與 promise2。
在 Node 中同樣會(huì)先執(zhí)行 promise3 再執(zhí)行 timer1寂呛,但是 timer1 之后會(huì)接著執(zhí)行 timer2怎诫,因?yàn)檫@是在 timer 階段。在 timer1 與 timer2 中產(chǎn)生的兩個(gè)微任務(wù)會(huì)在 timer 階段結(jié)束后依次執(zhí)行贷痪。