寫這篇文章的緣由是由於,這幾天在看 core-js
的源碼,而後發現了 queueMicrotask
的實現。因爲以前作的項目,對於微任務的執行需求,通常是使用 asap
這個庫來完成的,若是沒有使用這個庫的話,簡易版本能夠經過 Promise.resolve()
來代替,並無接觸過這個 api
,因此就想着抽時間研究一下。前端
通常看這種偏 web 標準的新的 nodeapi
,確定上來要先看兼容性的,我去 caniuse
查了一下,wtf? 居然搜索無結果。(詳見 issue)
而後只能去 MDN 來看一下了,大概是下圖這個樣子:git
昨天多是拼寫錯誤吧,受網友指點,來補充一下 caniuse 上面的截圖:github
能夠發現仍是比較新的 api
,若是要在項目中直接使用的話,仍是建議導入 polyfill
或者使用 asap
這個庫來實現相似的需求。web
api
?從微任務自己的概念來講的話,就是當咱們指望某段代碼,不阻塞當前執行的同步代碼,同時又指望它儘量快地執行時,咱們就須要它(這裏再也不贅述微任務的概念,能夠參考這篇文章)。api
通常狀況下,若是是編寫業務代碼,我覺的不多會遇到這樣的需求,惟一能想到的狀況可能存在於一些對即時反饋有性能要求的場景,好比搜索,當輸入關鍵字後發送異步請求獲取搜索信息以後,咱們可能會在前端對搜索結果進行一些處理,好比排序或者分組,可是這些操做可能不是優先級最高的任務,但它們又比較耗時(好比排序),所以咱們可能指望推遲它們的執行,但又指望它們儘量早地執行。瀏覽器
在閱讀一些著名框架或者工具庫的過程當中,我發現不少狀況下做者都會遇到這個需求,通常都經過 process.nextTick
或者 Promise.resolve
來解決。bash
setTimeout
的區別?本質上的區別應該在它們的執行時機上,而執行時機上的區別,本質上就是微任務和宏任務的區別。能夠直接打開控制檯運行一下如下的代碼:數據結構
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');
});
複製代碼
會獲得和上面代碼同樣的運行結果。
這裏引用 Explainer: queueMicrotask 的一些觀點來進行闡述:
Promise.resolve
會將異常轉化爲一個 rejected
的 Promise
Promise.resolve
會返回一個 Promise
實例對象,而直接 queueMicrotask
則不會api
可供使用,好比宏任務、RAF
setTimeout(callback, 0)
- 宏任務requestAnimationFrame(callback)
- RAFqueueMicrotask(callback)
- 微任務因爲它是一個用於指派微任務的底層 api
,咱們極可能會在其中無限制地指派微任務到其隊列之中,這樣作的效果就是,瀏覽器的微任務隊列始終處於非空狀態,這將致使控制權始終沒法交還給瀏覽器進行下一次事件循環,而後它就卡死了。
你能夠執行下面的代碼來體驗這個現象:
function infiniteEnqueue(fn) {
queueMicrotask(() => infiniteEnqueue(fn))
}
infiniteEnqueue(()=>{})
複製代碼
執行這段代碼會使瀏覽器當前的 tab
卡死,請慎用,建議先打開瀏覽器提供的進程管理窗口以供強制關閉卡死窗口。
這裏簡單闡述 MDN
上的和 core-js
中的模擬方案。
MDN 上的 polyfill 實現比較簡單粗暴,其實和直接調用 Promise.resolve
沒什麼區別,只是會在 .catch
中捕獲錯誤以後再拋出。
相比較 MDN 的實現,core-js
會複雜一些,它同時考慮了 nodejs
和 browser
兩種狀況,同時利用鏈表數據結構來模擬微任務隊列的執行單元,同時實現了一個 flush
方法表示執行所有的微任務單元。
還實現了一個 notify
方法,該方法會根據具體的 js
運行時環境以及 api
的支持狀況,分別嘗試使用 process.nextTick
、MutationObserver
和 Promise.resolve
以及最基本的宏任務 api
來執行 flush
方法,變相模擬微任務的執行過程。
關注公衆號 全棧_101,只談技術,不談人生。
業餘時間接手各類規模的外包項目,有意者私信。