js中的宏任務(wù)與微任務(wù)
時(shí)間:2022-08-13 03:15:01 | 來源:網(wǎng)站運(yùn)營
時(shí)間:2022-08-13 03:15:01 來源:網(wǎng)站運(yùn)營
事件循環(huán)
JavaScript 語言的一大特點(diǎn)就是單線程,也就是說,同一個時(shí)間只能做一件事。為了協(xié)調(diào)事件、用戶交互、腳本、UI 渲染和網(wǎng)絡(luò)處理等行為,防止主線程的不阻塞,Event Loop 的方案應(yīng)用而生。Event Loop 包含兩類:一類是基于 Browsing Context,一種是基于 Worker。二者的運(yùn)行是獨(dú)立的,也就是說,每一個 JavaScript 運(yùn)行的"線程環(huán)境"都有一個獨(dú)立的 Event Loop,每一個 Web Worker 也有一個獨(dú)立的 Event Loop。
本文所涉及到的事件循環(huán)是基于 Browsing Context。
任務(wù)隊(duì)列
根據(jù)規(guī)范,事件循環(huán)是通過任務(wù)隊(duì)列的機(jī)制來進(jìn)行協(xié)調(diào)的。一個 Event Loop 中,可以有一個或者多個任務(wù)隊(duì)列(task queue),一個任務(wù)隊(duì)列便是一系列有序任務(wù)(task)的集合;每個任務(wù)都有一個任務(wù)源(task source),源自同一個任務(wù)源的 task 必須放到同一個任務(wù)隊(duì)列,從不同源來的則被添加到不同隊(duì)列。setTimeout/Promise 等API便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)。
在事件循環(huán)中,每進(jìn)行一次循環(huán)操作稱為 tick,每一次 tick 的任務(wù)處理模型是比較復(fù)雜的,但關(guān)鍵步驟如下:
- 在此次 tick 中選擇最先進(jìn)入隊(duì)列的任務(wù)(oldest task),如果有則執(zhí)行(一次)
- 檢查是否存在 Microtasks,如果存在則不停地執(zhí)行,直至清空 Microtasks Queue
- 更新 render
- 主線程重復(fù)執(zhí)行上述步驟
在上訴tick的基礎(chǔ)上需要了解幾點(diǎn):
- JS分為同步任務(wù)和異步任務(wù)
- 同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧
- 主線程之外,事件觸發(fā)線程管理著一個任務(wù)隊(duì)列,只要異步任務(wù)有了運(yùn)行結(jié)果,就在任務(wù)隊(duì)列之中放置一個事件。
- 一旦執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢(此時(shí)JS引擎空閑),系統(tǒng)就會讀取任務(wù)隊(duì)列,將可運(yùn)行的異步任務(wù)添加到可執(zhí)行棧中,開始執(zhí)行。
宏任務(wù)
(macro)task,可以理解是每次執(zhí)行棧執(zhí)行的代碼就是一個宏任務(wù)(包括每次從事件隊(duì)列中獲取一個事件回調(diào)并放到執(zhí)行棧中執(zhí)行)。
瀏覽器為了能夠使得JS內(nèi)部(macro)task與DOM任務(wù)能夠有序的執(zhí)行,會在一個(macro)task執(zhí)行結(jié)束后,在下一個(macro)task 執(zhí)行開始前,對頁面進(jìn)行重新渲染,流程如下:
(macro)task->渲染->(macro)task->...
宏任務(wù)包含:
script(整體代碼)setTimeoutsetIntervalI/OUI交互事件postMessageMessageChannelsetImmediate(Node.js 環(huán)境)
微任務(wù)
microtask,可以理解是在當(dāng)前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù)。也就是說,在當(dāng)前task任務(wù)后,下一個task之前,在渲染之前。
所以它的響應(yīng)速度相比setTimeout(setTimeout是task)會更快,因?yàn)闊o需等渲染。也就是說,在某一個macrotask執(zhí)行完后,就會將在它執(zhí)行期間產(chǎn)生的所有microtask都執(zhí)行完畢(在渲染前)。
微任務(wù)包含:
Promise.thenObject.observeMutationObserverprocess.nextTick(Node.js 環(huán)境)
運(yùn)行機(jī)制
在事件循環(huán)中,每進(jìn)行一次循環(huán)操作稱為 tick,每一次 tick 的任務(wù)處理模型是比較復(fù)雜的,但關(guān)鍵步驟如下:
- 執(zhí)行一個宏任務(wù)(棧中沒有就從事件隊(duì)列中獲?。?/li>
- 執(zhí)行過程中如果遇到微任務(wù),就將它添加到微任務(wù)的任務(wù)隊(duì)列中
- 宏任務(wù)執(zhí)行完畢后,立即執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有微任務(wù)(依次執(zhí)行)
- 當(dāng)前宏任務(wù)執(zhí)行完畢,開始檢查渲染,然后GUI線程接管渲染
- 渲染完畢后,JS線程繼續(xù)接管,開始下一個宏任務(wù)(從事件隊(duì)列中獲?。?/li>
如圖: