queueMicrotask 來執行微任務
queueMicrotask 來執行微任務,Window 或 Worker 接口的 queueMicrotask() 方法,瀏覽器兼容性如下圖所示。
在不支持 queueMicrotask 方法的瀏覽器裏面,我們需要使用 polyfill 模擬實現。
下面的代碼是一份 queueMicrotask() 的 polyfill。它通過使用立即 resolve 的 promise 創建一個微任務(microtask),如果無法創建 promise,則回落(fallback)到使用 setTimeout()。
if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}
爲什麼我們需要這個 api?
從微任務本身的概念來說的話,就是當我們期望某段代碼,不阻塞當前執行的同步代碼,同時又期望它儘可能快地執行時,我們就需要它。
一般情況下,如果是編寫業務代碼,我覺得很少會遇到這樣的需求,唯一能想到的情況可能存在於一些對即時反饋有性能要求的場景,比如搜索,當輸入關鍵字後發送異步請求獲取搜索信息之後,我們可能會在前端對搜索結果進行一些處理,比如排序或者分組,但是這些操作可能不是優先級最高的任務,但它們又比較耗時(比如排序),因此我們可能期望推遲它們的執行,但又期望它們儘可能早地執行。
setTimeout(() => {
console.log('setTimeout');
}, 0);
queueMicrotask(() => {
console.log('queueMicrotask');
});
運行結果不出意外應該是:
queueMicrotask
setTimeout
如果你熟悉 nodejs 的話,應該和 process.nextTick 是類似的。
使用其他方式進行模擬所帶來的問題?
就是既然我們已經可以通過別的方式來模擬微任務的執行,我們還需要這個 api 幹什麼?比如,通過下面的代碼:
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('queueMicrotask');
});
會得到和上面代碼一樣的運行結果。
因爲微任務自身可以入列更多的微任務,且事件循環會持續處理微任務直至隊列爲空,那麼就存在一種使得事件循環無盡處理微任務的真實風險。如何處理遞歸增加微任務是要謹慎而行的。
如果可能的話,大部分開發者並不應該過多的使用微任務。在基於現代瀏覽器的 JavaScript 開發中有一個高度專業化的特性,那就是允許你調度代碼跳轉到其他事情之前,而那些事情原本是處於用戶計算機中一大堆等待發生的事情集合之中的。濫用這種能力將帶來性能問題。
通過引入 queueMicrotask(),可以避免通過 promise 去創建微任務而帶來的風險。舉例來說,當使用 promise 創建微任務時,由回調拋出的異常被報告爲 rejected promises 而不是標準異常。同時,創建和銷燬 promise 帶來了事件和內存方面的額外開銷,這是正確入列微任務的函數應該避免的。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/R8BGj9OeGWc1_NU-lI3bxQ