一.window.requestIdleCallback
1.作用:
這個方法將會在瀏覽器的空閑時間調(diào)用函數(shù)排隊魁袜。這樣可以使操作者在主事件循環(huán)上執(zhí)行后臺和低優(yōu)先級工作并淋,而不會影響延遲關(guān)鍵事件持舆,比如動畫和輸入響應(yīng),函數(shù)一般會按照先進(jìn)先執(zhí)行的順序進(jìn)行調(diào)用,但是如果設(shè)置了timeout(超時執(zhí)行時間)呆奕,將會導(dǎo)致為了在超時之前執(zhí)行相應(yīng)的函數(shù)操作而打亂執(zhí)行順序峰尝。
2.使用方法
window.requestIdleCallback(callback,option)
參數(shù):
callback:一個在瀏覽器空閑時即將被調(diào)用的函數(shù)引用函數(shù)會接收到一個名為IdleDeadline的參數(shù)偏窝,這個參數(shù)可以獲取當(dāng)前空閑時間以及回調(diào)是否在超時時間前已經(jīng)執(zhí)行的狀態(tài)。(IdleDeadline對象包括didTimeout武学,是一個布爾值表示任務(wù)是否超時祭往;以及timeRemaining(),表示當(dāng)前幀剩余的時間火窒,是留給任務(wù)執(zhí)行的時間)
option:配置項硼补,可以配置timeout,是可選參數(shù)(timeout:如果指定了timeout并具有一個正值熏矿,并且尚未通過超時毫秒數(shù)調(diào)用回調(diào)已骇,那么回調(diào)會在下一次空閑時期被強(qiáng)制執(zhí)行离钝,盡管這樣很可能會對性能造成負(fù)面影響。)
返回值:
一個ID褪储,可以通過window.cancelIdleCallback()來結(jié)束回調(diào)
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
// 任務(wù)隊列
const tasks = [
() => {
console.log("第一個任務(wù)");
},
() => {
console.log("第二個任務(wù)");
},
() => {
console.log("第三個任務(wù)");
},
];
function myNonEssentialWork (deadline) {
// 如果幀內(nèi)有富余的時間卵渴,或者超時
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
work();
}
if (tasks.length > 0)
requestIdleCallback(myNonEssentialWork);
}
function work () {
tasks.shift()();
console.log('執(zhí)行任務(wù)');
}
3.使用setTimeout模擬實現(xiàn)
window.requestIdleCallback = window.requestIdleCallback || function(handler) {
let startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining: function() {
return Math.max(0, 50.0 - (Date.now() - startTime));
}
});
}, 1);
}
可以在 ric 中執(zhí)行任務(wù)時需要注意以下幾點:
1.執(zhí)行重計算而非緊急任務(wù)
2.空閑回調(diào)執(zhí)行時間應(yīng)該小于 50ms,最好更少
3.空閑回調(diào)中不要操作 DOM鲤竹,因為它本來就是利用的重拍重繪后的間隙空閑時間浪读,重新操作 DOM 又會造成重拍重繪,DOM 操作建議在 rAF 中進(jìn)行辛藻。同時碘橘,操作 DOM 所需要的耗時是不確定的,因為會導(dǎo)致重新計算布局和視圖的繪制揩尸,所以這類操作不具備可預(yù)測性蛹屿。
4.Promise 也不建議在這里面進(jìn)行,因為 Promise 的回調(diào)屬性 Event loop 中優(yōu)先級較高的一種微任務(wù)岩榆,會在 requestIdleCallback 結(jié)束時立即執(zhí)行错负,不管此時是否還有富余的時間,這樣有很大可能會讓一幀超過 16 ms勇边。
React 的時間分片便是基于類似 requestIdleCallback 而實現(xiàn)犹撒,然而因為 ric 的兼容性及 50ms 流暢問題,React 自制了一個實現(xiàn): scheduler
二.window.requestAnimationFrame
1.作用:
通知瀏覽器粒褒,要求在下一次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫识颊。如果希望在下一次重繪之前更新下一幀動畫,那么需要回調(diào)函數(shù)自身必須再次調(diào)用window.requestAnimationFrame(),當(dāng)window.requestAnimationFrame運(yùn)行在后臺標(biāo)簽或者隱藏的iframe時奕坟,會被暫停來提高性能
2.用法
window.requestAnimationFrame(callback)
參數(shù):
callback:下一次重繪之前更新動畫幀所調(diào)用的函數(shù)祥款。該回調(diào)函數(shù)會被傳入DOMHighResTimeStamp參數(shù),它表示的是開始執(zhí)行回調(diào)函數(shù)的時刻
返回值:
一個ID月杉,可以傳入window.cancelAnimationFrame()以取消回調(diào)函數(shù)刃跛。
3.補(bǔ)充
以往我們執(zhí)行動畫動畫時所采用的操作是setTimeout和setInterval,這種做法的弊端就是:回調(diào)函數(shù)執(zhí)行時間是不固定的,有可能剛好就卡在末尾或者不再執(zhí)行了苛萎,會引起丟幀和卡頓
歸根到底發(fā)生上面這個問題的原因在于時機(jī)桨昙,也就是瀏覽器要知道何時對回調(diào)函數(shù)進(jìn)行響應(yīng)。
setTimeout
或setInterval
是使用定時器來觸發(fā)回調(diào)函數(shù)的腌歉,而定時器并無法保證能夠準(zhǔn)確無誤的執(zhí)行蛙酪,有許多因素會影響它的運(yùn)行時機(jī),比如說:當(dāng)有同步代碼執(zhí)行時翘盖,會先等同步代碼執(zhí)行完畢桂塞,異步隊列中沒有其他任務(wù),才會輪到自己執(zhí)行馍驯。并且藐俺,我們知道每一次重新渲染的最佳時間大約是 16.6 ms炊甲,如果定時器的時間間隔過短,就會造成 過度渲染欲芹,增加開銷;過長又會延遲渲染吟吝,使動畫不流暢菱父。
requestAnimationFrame 方法不同與 setTimeout 或 setInterval,它是由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機(jī)的剑逃,會請求瀏覽器在下一次重新渲染之前執(zhí)行回調(diào)函數(shù)浙宜。無論設(shè)備的刷新率是多少,requestAnimationFrame 的時間間隔都會緊跟屏幕刷新一次所需要的時間蛹磺;例如某一設(shè)備的刷新率是 75 Hz粟瞬,那這時的時間間隔就是 13.3 ms(1 秒 / 75 次)。需要注意的是這個方法雖然能夠保證回調(diào)函數(shù)在每一幀內(nèi)只渲染一次萤捆,但是如果這一幀有太多任務(wù)執(zhí)行裙品,還是會造成卡頓的;因此它只能保證重新渲染的時間間隔最短是屏幕的刷新時間俗或。