十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊
量身定制 + 運營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
JavaScript 事件循環(huán)中微任務(wù)和宏任務(wù)有什么區(qū)別,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、江城網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5開發(fā)、購物商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為江城等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
事件循環(huán):微任務(wù)和宏任務(wù)
瀏覽器中 JavaScript 的執(zhí)行流程和 Node.js 中的流程都是基于 事件循環(huán) 的。
理解事件循環(huán)的工作方式對于代碼優(yōu)化很重要,有時對于正確的架構(gòu)也很重要。
在本章中,我們首先介紹有關(guān)事件循環(huán)工作方式的理論細(xì)節(jié),然后介紹該知識的實際應(yīng)用。
事件循環(huán)
事件循環(huán) 的概念非常簡單。它是一個在 JavaScript 引擎等待任務(wù),執(zhí)行任務(wù)和進(jìn)入休眠狀態(tài)等待更多任務(wù)這幾個狀態(tài)之間轉(zhuǎn)換的無限循環(huán)。
引擎的一般算法:
1.當(dāng)有任務(wù)時:
從最先進(jìn)入的任務(wù)開始執(zhí)行。
2.休眠直到出現(xiàn)任務(wù),然后轉(zhuǎn)到第 1 步。
當(dāng)我們?yōu)g覽一個網(wǎng)頁時就是上述這種形式。JavaScript 引擎大多數(shù)時候不執(zhí)行任何操作,它僅在腳本/處理程序/事件激活時執(zhí)行。
任務(wù)示例:
當(dāng)外部腳本
……但是我們也可能想在任務(wù)執(zhí)行期間展示一些東西,例如進(jìn)度條。
如果我們使用 setTimeout 將繁重的任務(wù)拆分成幾部分,那么變化就會被在它們之間繪制出來。
這看起來更好看:
現(xiàn)在 div 顯示了 i 的值的增長,這就是進(jìn)度條的一種。
用例 3:在事件之后做一些事情
在事件處理程序中,我們可能會決定推遲某些行為,直到事件冒泡并在所有級別上得到處理后。我們可以通過將該代碼包裝到零延遲的 setTimeout 中來做到這一點。
在 創(chuàng)建自定義事件[1] 一章中,我們看到過這樣一個例子:自定義事件 menu-open 被在 setTimeout 中分派(dispatched),所以它在 click 事件被處理完成之后發(fā)生。
menu.onclick = function() { // ... // 創(chuàng)建一個具有被點擊的菜單項的數(shù)據(jù)的自定義事件 let customEvent = new CustomEvent("menu-open", { bubbles: true }); // 異步分派(dispatch)自定義事件 setTimeout(() => menu.dispatchEvent(customEvent)); };宏任務(wù)和微任務(wù)
除了本章中所講的 宏任務(wù)(macrotask) 外,還有在 微任務(wù)隊列[2] 一章中提到的 微任務(wù)(microtask)。
微任務(wù)僅來自于我們的代碼。它們通常是由 promise 創(chuàng)建的:對 .then/catch/finally 處理程序的執(zhí)行會成為微任務(wù)。微任務(wù)也被用于 await 的“幕后”,因為它是 promise 處理的另一種形式。
還有一個特殊的函數(shù) queueMicrotask(func),它對 func 進(jìn)行排隊,以在微任務(wù)隊列中執(zhí)行。
每個宏任務(wù)之后,引擎會立即執(zhí)行微任務(wù)隊列中的所有任務(wù),然后再執(zhí)行其他的宏任務(wù),或渲染,或進(jìn)行其他任何操作。
例如,看看下面這個示例:
setTimeout(() => alert("timeout")); Promise.resolve() .then(() => alert("promise")); alert("code");這里的執(zhí)行順序是怎樣的?
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
code 首先顯示,因為它是常規(guī)的同步調(diào)用。
promise 第二個出現(xiàn),因為 then 會通過微任務(wù)隊列,并在當(dāng)前代碼之后執(zhí)行。
timeout 最后顯示,因為它是一個宏任務(wù)。
更詳細(xì)的事件循環(huán)圖示如下(順序是從上到下,即:首先是腳本,然后是微任務(wù),渲染等):

微任務(wù)會在執(zhí)行任何其他事件處理,或渲染,或執(zhí)行任何其他宏任務(wù)之前完成。
這很重要,因為它確保了微任務(wù)之間的應(yīng)用程序環(huán)境基本相同(沒有鼠標(biāo)坐標(biāo)更改,沒有新的網(wǎng)絡(luò)數(shù)據(jù)等)。
如果我們想要異步執(zhí)行(在當(dāng)前代碼之后)一個函數(shù),但是要在更改被渲染或新事件被處理之前執(zhí)行,那么我們可以使用 queueMicrotask 來對其進(jìn)行安排(schedule)。
這是一個與前面那個例子類似的,帶有“計數(shù)進(jìn)度條”的示例,但是它使用了 queueMicrotask而不是 setTimeout。你可以看到它在最后才渲染。就像寫的是同步代碼一樣:
總結(jié)
更詳細(xì)的事件循環(huán)算法(盡管與 規(guī)范[3] 相比仍然是簡化過的):
1.從 宏任務(wù) 隊列(例如 "script")中出隊(dequeue)并執(zhí)行最早的任務(wù)。
2.執(zhí)行所有 微任務(wù):
出隊(dequeue)并執(zhí)行最早的微任務(wù)。
當(dāng)微任務(wù)隊列非空時:
3.執(zhí)行渲染,如果有。
4.如果宏任務(wù)隊列為空,則休眠直到出現(xiàn)宏任務(wù)。
5.轉(zhuǎn)到步驟 1。
安排(schedule)一個新的 宏任務(wù):
使用零延遲的 setTimeout(f)。
它可被用于將繁重的計算任務(wù)拆分成多個部分,以使瀏覽器能夠?qū)τ脩羰录鞒龇磻?yīng),并在任務(wù)的各部分之間顯示任務(wù)進(jìn)度。
此外,也被用于在事件處理程序中,將一個行為(action)安排(schedule)在事件被完全處理(冒泡完成)后。
安排一個新的 微任務(wù):
使用 queueMicrotask(f)。
promise 處理程序也會通過微任務(wù)隊列。
在微任務(wù)之間沒有 UI 或網(wǎng)絡(luò)事件的處理:它們一個立即接一個地執(zhí)行。
所以,我們可以使用 queueMicrotask 來在保持環(huán)境狀態(tài)一致的情況下,異步地執(zhí)行一個函數(shù)。
Web Workers:
對于不應(yīng)該阻塞事件循環(huán)的耗時長的繁重計算任務(wù),我們可以使用 Web Workers[4]。
這是在另一個并行線程中運行代碼的方式。
Web Workers 可以與主線程交換消息,但是它們具有自己的變量和事件循環(huán)。
Web Workers 沒有訪問 DOM 的權(quán)限,因此,它們對于同時使用多個 CPU 內(nèi)核的計算非常有用。
看完上述內(nèi)容,你們掌握 JavaScript 事件循環(huán)中微任務(wù)和宏任務(wù)有什么區(qū)別的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!