咱們都知道React 16實現了新的調度策略(Fiber), 新的調度策略提到的異步、可中斷,其實就是基於瀏覽器的 requestIdleCallback和requestAnimationFrame兩個API。因此這裏咱們有必要了解一下這兩個API,關於Fiber部分後面會單開幾篇講。react
當關注用戶體驗,不但願由於一些不重要的任務(如統計上報)致使用戶感受到卡頓的話,就應該考慮使用requestIdleCallback。由於requestIdleCallback回調的執行的前提條件是當前瀏覽器處於空閒狀態。web
requestIdleCallback will schedule work when there is free time at the end of a frame, or when the user is inactive.npm
requestIdleCallback用法示例瀏覽器
requestIdelCallback(myNonEssentialWork);
function myNonEssentialWork (deadline) {
// deadline.timeRemaining()能夠獲取到當前幀剩餘時間
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0){
requestIdleCallback(myNonEssentialWork);
}
}
複製代碼
requestAnimationFrame的回調會在每一幀肯定執行,屬於高優先級任務,而requestIdleCallback的回調則不必定,屬於低優先級任務。 咱們所看到的網頁,都是瀏覽器一幀一幀繪製出來的,一般認爲FPS爲60的時候是比較流暢的,而FPS爲個位數的時候就屬於用戶能夠感知到的卡頓了,那麼在一幀裏面瀏覽器都要作哪些事情呢,以下所示:bash
圖中一幀包含了用戶的交互、js的執行、以及requestAnimationFrame的調用,佈局計算以及頁面的重繪等工做。 假如某一幀裏面要執行的任務很少,在不到16ms(1000/60)的時間內就完成了上述任務的話,那麼這一幀就會有必定的空閒時間,這段時間就剛好能夠用來執行requestIdleCallback的回調,以下圖所示:dom
因爲requestIdleCallback利用的是幀的空閒時間,因此就有可能出現瀏覽器一直處於繁忙狀態,致使回調一直沒法執行,這其實也並非咱們指望的結果(如上報丟失),那麼這種狀況咱們就須要在調用requestIdleCallback的時候傳入第二個配置參數timeout了?異步
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
function myNonEssentialWork (deadline) {
// 當回調函數是因爲超時才得以執行的話,deadline.didTimeout爲true
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0) {
requestIdleCallback(myNonEssentialWork);
}
}
複製代碼
若是是由於timeout回調才得以執行的話,其實用戶就有可能會感受到卡頓了,由於一幀的執行時間必然已經超過16ms了函數
強烈建議不要,從上面一幀的構成裏面能夠看到,requestIdleCallback回調的執行說明前面的工做(包括樣式變動以及佈局計算)都已完成。若是咱們在callback裏面作DOM修改的話,以前所作的佈局計算都會失效,並且若是下一幀裏有獲取佈局(如getBoundingClientRect、clientWidth)等操做的話,瀏覽器就不得不執行強制重排工做,這會極大的影響性能,另外因爲修改dom操做的時間是不可預測的,所以很容易超出當前幀空閒時間的閾值,故而不推薦這麼作。推薦的作法是在requestAnimationFrame裏面作dom的修改,能夠在requestIdleCallback裏面構建Document Fragment,而後在下一幀的requestAnimationFrame裏面應用Fragment。佈局
除了不推薦DOM修改操做外,Promise的resolve(reject)操做也不建議放在裏面,由於Promise的回調會在idle的回調執行完成後馬上執行,會拉長當前幀的耗時,因此不推薦。性能
推薦放在requestIdleCallback裏面的應該是小塊的(microTask)而且可預測時間的任務。關於microTask推薦看這裏
推薦使用npm包request-idle-callback
https://developers.google.com/web/updates/2015/08/using-requestidlecallback https://medium.com/@paul_irish/requestanimationframe-scheduling-for-nerds-9c57f7438ef4 https://juejin.im/entry/59082301a22b9d0065f1a186 https://insights.thoughtworks.cn/react-fiber/