繼上篇 宏任務(wù)與微任務(wù)

經(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í)間。

參考鏈接:https://blog.csdn.net/qq_41257129/article/details/100743394?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-2-100743394.nonecase

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。