歡迎關注個人公衆號睿Talk
,獲取我最新的文章:javascript
Promise
, setTimeout
, requestAnimationFrame
, requestIdleCallback
這幾個概念相信不少人都很熟悉了,最近在看 React Fiber
源碼的時候又對它們有了更深一層的認識,在此分享一下。下文將用 rAF
表明 requestAnimationFrame
, rIC
表明 requestIdleCallback
。java
事件循環和上面 4 個名詞的基本概念在此再也不囉嗦了,咱們着重看下它們之間的關係。瀏覽器是一個 UI 系統,全部的操做最終都會以頁面的形式展示,而頁面的基本單位是幀。一幀中可能包括的任務有下面幾種類型。segmentfault
setTimeout
Promise
requestAnimationFrame
requestIdleCallback
理想狀況下,頁面會以 60 幀每秒的幀率來運行,但實際上每秒繪製多少幀是由多個因素決定的,下面舉一些例子:promise
rAF
製做動畫的時候,瀏覽器會盡量快的重繪頁面,桌面瀏覽器多是 60 幀,移動瀏覽器多是 30 幀。從上面的例子能夠看出,頁面的幀率不是固定的,是會動態變化的。當某一幀的任務佔用大量時間的時候,會影響到下一幀的執行。那麼誰來調節幀率呢?顯然只能依靠瀏覽器自身。做爲開發者的咱們是沒法準確預知回調何時執行的。好比:瀏覽器
function animation() { console.log('time: ', +new Date()); setTimeout(animate, 1000 / 60); } animation();
上面的函數假定了瀏覽器以幀率 60 來運行,但當幀率達不到的時候,2 幀之間回調可能執行了屢次,也可能一次都不執行,簡稱掉幀。函數
因此在製做動畫的時候,咱們不能預設瀏覽器的幀率,正確的作法是經過 rAF
註冊回調, 由瀏覽器來控制動畫調用時機:oop
function animation() { console.log('time: ', +new Date()); requestAnimationFrame(animation); } animation();
rAF
會保證註冊的回調在下次渲染頁面以前執行,且只會執行一次。另外,當頁面處於不可見狀態時,rAF
會自動中止執行,以節省系統資源。佈局
Promise
, setTimeout
, rAF
和 rIC
對應 4 種隊列:微任務隊列、宏任務隊列、animation 隊列和 idle 隊列。性能
setTimeout(()=>console.log('setTimeout'), 0); Promise.resolve().then(()=>console.log('promise')); requestAnimationFrame(()=>console.log('animation')); requestIdleCallback(()=>console.log('idle')); // 執行結果大多數狀況下是: promise, animation, setTimeout, idle // 少數狀況是:promise, setTimeout, animation, idle
再來談談空閒時間怎麼理解。假設在 1 秒內有 3 幀須要渲染:優化
rAF
佔用的時間很少,有大量的空閒時間與rAF
相似,rIC
的執行時機是由瀏覽器控制的,能更好的保證體驗,優化性能。通常優先級高的任務(如 UI 更新)會放在 rAF
隊列,優先級低的任務(如日誌上傳)會放 rIC
。
在一個事件循環內,各個隊列有如下特性:
function loop() { Promise.resolve().then(loop); } loop();
React Fiber
就是用這個機制。但最新版的 React Fiber
已經不用 rIC
了,由於調用的頻率過低,改用 rAF
了 本文介紹了 4 種隊列的執行順序和每一個隊列的特性,它們是:宏任務隊列、微任務隊列、animation 隊列和 idle 隊列。實際應用時能夠根據它們各自的特色分配不一樣的任務。