一篇文章帶你搞懂JavaScript 微任務(Microtask)

點擊上方“ 前端進階學習交流 ”,進行關注

回覆“前端”即可獲贈前端相關學習資料

操吳戈兮被犀甲,車錯轂兮短兵接。

大家好,我進階學習者。

一、前言

Promise 處理始終是異步的,因爲所有 promise 行爲都會通過內部的 “promise jobs” 隊列,也被稱爲“微任務隊列”。

Promise 的處理程序(handlers).then、.catch 和 .finally 都是異步的。

即便一個 promise 立即被 resolve,.then、.catch 和 .finally ,下面的代碼也會在這些處理程序(handler)之前被執行。

代碼如下:

let promise = Promise.resolve();promise.then(() => alert("promise done!"));alert("code finished"); // 這個 alert 先顯示

如果運行它,會首先看到 code finished,然後纔是 promise done。這很奇怪,因爲這個 promise 肯定是一開始就完成的。

運行結果:

爲什麼 .then 會在之後才被觸發?這是怎麼回事?


二、微任務隊列(Microtask queue)

1. 如果執行順序對很重要該怎麼辦?

Promise 的處理程序(handler)總是會經過這個內部隊列。

如果有一個包含多個 .then/catch/finally 的鏈,那麼它們中的每一個都是異步執行的。也就是說,它會首先進入隊列。

然後在當前代碼執行完成並且先前排隊的處理程序(handler)都完成時纔會被執行。

2. 怎麼才能讓 code finished 在 promise done 之後運行呢?

很簡單,只需要像下面這樣使用 .then 將其放入隊列:

Promise.resolve()  .then(() => alert("promise done!")); //規定相對應的順序  .then(() => alert("code finished"));

上面代碼,加上這語句就是按照預期執行的。


三、未處理的 rejection

現在,可以確切地看到 JavaScript 是如何發現未處理的 rejection 的。

如果一個 promise 的 error 未被在微*任務*隊列的末尾進行處理,則會出現“未處理的 rejection”。

正常來說。

如果預期可能會發生錯誤,會在 promise 鏈上添加 .catch 來處理 error:

let promise = Promise.reject(new Error("Promise Failed!"));promise.catch(err => alert('caught'));// 不會運行:error 已經被處理window.addEventListener('unhandledrejection', event => alert(event.reason));

運行結果:

但是如果忘記添加 .catch,那麼,微任務隊列清空後,JavaScript 引擎會觸發下面這事件:

let promise = Promise.reject(new Error("Promise Failed!"));// Promise Failed!window.addEventListener('unhandledrejection', event => alert(event.reason));

運行結果:

如果遲一點再處理這個 error 會怎樣?

例:

let promise = Promise.reject(new Error("Promise Failed!"));setTimeout(() => promise.catch(err => alert('caught')), 1000);// Error: Promise Failed!window.addEventListener('unhandledrejection', event => alert(event.reason));

現在,如果運行上面這段代碼,會先看到 Promise Failed!,然後纔是 caught。

注:

如果並不瞭解微任務隊列,可能會想:“爲什麼 unhandledrejection 處理程序(handler)會運行?已經捕獲(catch)並處理了 error!”。

當微任務隊列中的任務都完成時,纔會生成 unhandledrejection:引擎會檢查 promise,如果 promise 中的任意一個出現 “rejected” 狀態,unhandledrejection 事件就會被觸發。

在上面這個例子中,被添加到 setTimeout 中的 .catch 也會被觸發。只是會在 unhandledrejection 事件出現之後纔會被觸發。


四、總結

本文基於JavaScript基礎,介紹了微任務。其中.then/catch/finally 處理程序(handler),總是在當前代碼完成後纔會被調用。

如果需要確保一段代碼,在 .then/catch/finally 之後被執行,可以將它添加到鏈式調用的 .then 中。

在大多數 JavaScript 引擎中(包括瀏覽器和 Node.js),微任務(microtask)的概念與“事件循環(event loop)”和“宏任務(macrotasks)”緊密相關。

代碼很簡單,希望能夠幫助你更好的學習。

------------------- End -------------------

往期精彩文章推薦:

歡迎大家點贊,留言,轉發,轉載,感謝大家的相伴與支持

想加入前端學習羣請在後臺回覆【入羣

萬水千山總是情,點個【在看】行不行

本文分享自微信公衆號 - 前端進階學習交流(gh_cf4e462f0835)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章