前端優(yōu)化之異步更新(Micro-Task&Macro-Task)

? ? ? ? 各位看官我們又見(jiàn)面了,這幾天剛和媳婦拍完結(jié)婚照回來(lái)了,曬得跟非洲的猴子一樣~

? ? ? ? 總算有點(diǎn)時(shí)間了,打算再寫一篇博文,來(lái)掃一下我之前對(duì)異步隊(duì)列的盲點(diǎn),同時(shí)論證一下異步更新在Dom優(yōu)化時(shí)所起到的作用。


? ? ? ? 從前,在我的認(rèn)知里,js這個(gè)單線程的語(yǔ)言如果想做異步操作(ajax,setTimeout,onclick)的話,就需要將所有的異步事件安排到一組事件隊(duì)列中去,當(dāng)主線程的代碼全部跑完,再?gòu)年?duì)列中的去一個(gè)個(gè)執(zhí)行異步事件。我覺(jué)得與其叫做異步,倒不如叫它事件補(bǔ)充精準(zhǔn)吧~

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? —— 高級(jí)裝B工程師 Yubble

? ? ? ? 以上的結(jié)論還是沒(méi)有問(wèn)題的,不管去哪面試對(duì)方一問(wèn)到異步只要回答出上面這一段話效果就都是ok的(來(lái)自在下面試各大一線互聯(lián)網(wǎng)公司的親身經(jīng)歷,嗚嗚嗚~)

? ? ? ? 但是!我們是脫離了低級(jí)趣味的人,我是提倡裝就要裝出高逼格的人,如果客爺您能滔滔不絕的說(shuō)出異步隊(duì)列中包含的兩種隊(duì)列:macro (宏任務(wù)隊(duì)列) 和 micro (微任務(wù)隊(duì)列),那您很有可能成為面試官欽點(diǎn)的Mr.Right了,


? ? ? ? 大風(fēng)起兮云飛揚(yáng)~道是無(wú)晴卻有晴

? ? ? ? 首先看看這兩種異步事件的成員吧

? ? ? ? 常見(jiàn)的Micro-Task事件:process.nextTick(nodeJs)、Promise、MutationObserver 等。

? ? ? ? 常見(jiàn)的Macro-Task事件:setTimeout、setInterval、 setImmediate(nodeJs)、script(整體代碼)

? ? ? ? 有沒(méi)有覺(jué)得Macro-Task的事件都眼熟,Micro-Task的事件都臉兒生,也就是說(shuō)在Promise,MutationObserver這些h5新特性問(wèn)世之前,異步隊(duì)列一直都是Macro-Task一家說(shuō)了算,就是我們以前說(shuō)的任務(wù)隊(duì)列(task queue)!

? ? ? ? 那么問(wèn)題來(lái)了,這哥兒倆都是異步事件,在一次事件循環(huán)的過(guò)程中誰(shuí)先誰(shuí)后呢?首先我們來(lái)看一張Event Loop的周期示例圖:

? ? ? ? 是不是剛一看有點(diǎn)懵,在下剛開(kāi)始看到這張圖的時(shí)候是這樣的

? ? ? ? 先不要輕易放棄哦我的小可愛(ài)們,我們先簡(jiǎn)單的歸納一下這一次事件循環(huán)的周期都做了什么。

? ? ? ? 1,首先瀏覽器將macro隊(duì)列中的 一個(gè)任務(wù) 單獨(dú)出列執(zhí)行了,是一個(gè)任務(wù)哦


? ? ? ? 2,執(zhí)行完畢后緊接著micro隊(duì)列拋出 所有任務(wù) 依次執(zhí)行,是所有micro任務(wù)哦


? ? ? ? 3,到了這個(gè)節(jié)點(diǎn),如果異步函數(shù)中有對(duì)于dom的操作,便可以執(zhí)行了,dom變化了就會(huì)引起瀏覽器的回流與重繪


? ? ? ? 4,將worker線程中的任務(wù)釋放執(zhí)行出來(lái),這里就不多說(shuō)了~

? ? ? ? 他們的區(qū)別是很明顯的對(duì)嗎?在每一次的事件循環(huán)中macro的事件隊(duì)列是一個(gè)個(gè)的執(zhí)行的,然而每執(zhí)行完一個(gè)macro事件都會(huì)將所有的micro事件一同執(zhí)行,緊接著立即去做dom的變化~

? ? ? ? 我們來(lái)簡(jiǎn)單的做一個(gè)驗(yàn)證喉,我將macro異步的代表 setTimeout 與micro異步的代表 promise 穿插著寫在一起

? ? ? ? 這里也就不賣關(guān)子了,正確的輸出是

? ? ? ? 一目了然,不論我們將同一域中的macro和micro以什么順序編寫,micro的執(zhí)行順序都會(huì)在macro之前,并且是將所有的micro事件一起出隊(duì)。

? ? ? ? 這位客爺問(wèn)了,“上面不是說(shuō)先執(zhí)行macro的事件,再執(zhí)行micro事件嗎,你這個(gè)怎么micro比macro要優(yōu)先吶?”,特別好,您能看到這里我很有成就感,這是因?yàn)?b>script標(biāo)簽本就在macro隊(duì)列之中!下面在下就用我平時(shí)最通俗的大碴子話來(lái)給各位看官敘述一下這幾行代碼在瀏覽器運(yùn)行時(shí)的過(guò)程:

? ? ? ? 1, 瀏覽器運(yùn)行到<script>的狀態(tài):調(diào)用棧空。micro隊(duì)列為空,macro隊(duì)列中只有一個(gè)script腳本(包含script的全部?jī)?nèi)容)

? ? ? ? 2,<script>的代碼開(kāi)始執(zhí)行,在執(zhí)行script代碼中內(nèi)容的時(shí)候,將所有的異步代碼分成了macro與micro,并分別進(jìn)入隊(duì)列。等待同步代碼全部執(zhí)行完成

? ? ? ? 3,檢測(cè)到macro隊(duì)列中有事件在排隊(duì),第一個(gè)等待出隊(duì)的是<script>。注意了,此時(shí)是script從macro出隊(duì)了。

? ? ? ? 4,執(zhí)行完一個(gè)macro任務(wù),就該將整隊(duì)的micro任務(wù)出隊(duì)執(zhí)行了吧,于是乎在同步代碼執(zhí)行時(shí)被寫入micro隊(duì)列中的所有Promise就一窩蜂的跑了出來(lái)。

? ? ? ? 5,之后開(kāi)始進(jìn)行異步回調(diào)中的dom操作,觸發(fā)瀏覽器渲染,執(zhí)行worker,完成一個(gè)閉環(huán)。緊接著執(zhí)行下一個(gè)macro異步事件(setTimeout)

? ? ? ? 所以我們能夠清楚的是,macro隊(duì)列中有多少異步事件,我們的瀏覽器就要走多少次的3,4,5。


? ? ? ? 結(jié)論:

? ? ? ? ? ? ? ? 經(jīng)過(guò)了一番折騰我想各位客爺也明白我想表達(dá)的意思了,如果我們想要在同步代碼執(zhí)行完成之后用異步的方式來(lái)更新dom,盡量要寫在micro的異步事件中去,他是可以第一時(shí)間將dom變化觸發(fā)渲染的,你來(lái)了嗎田利小哥,不要再把異步更新dom寫到setTimeout上去啦。

? ? ? ? ? ? ? ? 如果各位客爺使用過(guò)vue的nextTick的話,不妨看一看它的源碼,在異步更新dom上,它就是這么優(yōu)化的,上圖~


? ? ? ? ? ? ? ? 閑聊幾句吧,最近我的第一個(gè)小程序正在做備案,馬上就要上線啦,打算用這個(gè)跟我女朋友求婚一舉拿下,哈哈,來(lái)自程序員的浪漫~

? ? ? ? ? ? ? ? 最后祝各位新年快樂(lè),來(lái)年發(fā)大財(cái),家家大豐收

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容