經(jīng)過查找資料和測(cè)試,發(fā)現(xiàn)現(xiàn)在的node和瀏覽器下的 宏任務(wù)與微任務(wù)的執(zhí)行順序沒有區(qū)別
左邊是瀏覽器,右邊是node(V12.18.2)?
但是,在node之前的版本宏任務(wù)/微任務(wù)和瀏覽器的執(zhí)行順序確實(shí)有差別 ,在把node降到v8.4.0時(shí) ,宏任務(wù)要執(zhí)行完一隊(duì)列的任務(wù),才會(huì)去執(zhí)行新的微任務(wù),而不是像我們前一篇 說到的,每執(zhí)行完一個(gè)宏任務(wù),就去檢測(cè)是否有微任務(wù)需要執(zhí)行
下圖運(yùn)行一段超長(zhǎng)代碼
運(yùn)行結(jié)果:14、15、1、2、4、16、8、8promise、8promise+then、9、5、6、10、11、12、3、7、13
宏任務(wù):setTimeOut、setImmediate,js代碼
微任務(wù):promise、process.nextTick
這段代碼,我們之前沒涉及到的知識(shí)點(diǎn)是:
1.微任務(wù)里process.nextTick,方便記憶,你可以理解為 它是微任務(wù)中的特殊隊(duì)列,每次執(zhí)行微任務(wù)要先執(zhí)行這個(gè)特殊隊(duì)列,隊(duì)列執(zhí)行完畢再執(zhí)行其他的微任務(wù)。
2.宏任務(wù)里的setTimeOut、setImmediate(在瀏覽器中不存在)的執(zhí)行順序。
node中的libuv庫負(fù)責(zé)Node API的執(zhí)行。它將不同的任務(wù)分配給不同的線程,形成一個(gè)
Event Loop,以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給V8引擎(盜圖)?
從圖中可以看出Node中事件循環(huán)的順序:
外部輸入數(shù)據(jù)–>輪詢階段(poll)–>檢查階段(check)–>關(guān)閉事件回調(diào)階段(close callback)–>定時(shí)器檢測(cè)階段(timer)–>I/O事件回調(diào)階段(I/O callbacks)–>閑置階段(idle, prepare)–>輪詢階段(按照該順序反復(fù)運(yùn)行)…
timers階段: 執(zhí)行timer(setTimeout 、setInterval)的回調(diào)
I/O callbacks階段: 處理一些上一輪循環(huán)中少數(shù)未執(zhí)行的I/O回調(diào)
idle,prepare階段: 僅Node內(nèi)部使用
poll階段: 獲取新的I/O事件,適當(dāng)?shù)臈l件下node將阻塞在這里。(發(fā)生阻塞的情況為:poll隊(duì)列為空,且沒有代碼設(shè)定為setImmediate())
check階段: 執(zhí)行setImmediate()的回調(diào)。(如果poll階段空閑,并且有被setImmediate()設(shè)定的回調(diào),那么事件循環(huán)直接跳到check執(zhí)行,而不是阻塞在poll階段等待回調(diào)被加入)
[
注意]:setImmediate()在這個(gè)階段具有最高優(yōu)先級(jí),只要poll隊(duì)列為空,代碼被setImmediate(),無論是否有timers達(dá)到下限時(shí)間,setImmediate()的代碼都先執(zhí)行。 (下面有例子會(huì)解釋)
close callbacks階段:執(zhí)行socket的close事件回調(diào)
setTimeout(() => {
? ? setImmediate(() => {
? ? ? ? console.log('setImmediate');
? ? },10);
? ? setTimeout(() => {
? ? ? ? console.log('setTimeout');
? ? }, 0);
}, 0);
在timers階段執(zhí)行外部的setTimeout之后,內(nèi)層setTimeout與setImmediate入隊(duì),時(shí)間循環(huán)繼續(xù)往下走,到poll階段發(fā)現(xiàn)隊(duì)列為空,此時(shí)代碼有setImmediate(),所以直接進(jìn)入check階段執(zhí)行setimmediate()的回調(diào)。之后再第二次時(shí)間循環(huán)的timers中再執(zhí)行相應(yīng)的回調(diào)。
總結(jié):
如果兩者都在主模塊中調(diào)用,那么執(zhí)行先后取決于進(jìn)程性能,也就是隨機(jī)。
如果兩者都不在主模塊調(diào)用(被一個(gè)異步操作包裹),那么setImmediate的回調(diào)永遠(yuǎn)先執(zhí)行。
(繼續(xù)盜圖)
總結(jié)
瀏覽器和Node 環(huán)境下,microtask 任務(wù)隊(duì)列的執(zhí)行時(shí)機(jī)不同
Node端,microtask 在事件循環(huán)的各個(gè)階段之間執(zhí)行
瀏覽器端,microtask 在事件循環(huán)的 macrotask 執(zhí)行完之后執(zhí)行
問題思考(個(gè)人見解,期待大佬們的高見)
1、為什么需要區(qū)分宏任務(wù)和微任務(wù)?
個(gè)人認(rèn)為,區(qū)分兩個(gè)隊(duì)列可以方便插隊(duì),或者說可以更加方便的操作異步任務(wù)的執(zhí)行順序。
2.當(dāng)所有任務(wù)清空之后,setTimeout(,1000000)長(zhǎng)時(shí)間的循環(huán)任務(wù)怎么處理?性能怎么樣
首先setTimeout定時(shí)任務(wù),是不會(huì)影響性能的,因?yàn)樵诘却臅r(shí)間里,它不去占用cpu。等時(shí)間到了,就會(huì)將它的回調(diào)壓入異步隊(duì)列中,等待主線程的提取。所以setTimeout的性能如何取決于它要執(zhí)行的內(nèi)容,而不是等待的時(shí)間。