认识
2024年03月06日
一、认识
window.requestIdleCallback()
方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间 timeout
,则有可能为了在超时前执行函数而打乱执行顺序。
window.requestIdleCallback()
底层是在一轮事件循环的最后执行。此时, 浏览器绘制完成, 判断执行栈
和微任务队列
是否都为空,如果是的话,则进行 Idle
空闲周期的算法,判断是否要执行 requestIdleCallback
的回调函数
二、语法
const callbackID = requestIdleCallback(callback)
const callbackID = requestIdleCallback(callback, options)
-
callback
: 一个在事件循环空闲时即将被调用的函数的引用。函数会接收到一个名为IdleDeadline
的参数,IdleDeadline
它提供了timeRemaining()
方法,用来判断用户代理预计还剩余多少闲置时间, 以及didTimeout
属性,用来判断当前的回调函数是否因超时而被执行。-
IdleDeadline.didTimeout
: 一个布尔值,如果回调是因为超过了设置的超时时间而被执行的,则其值为true
。 -
IdleDeadline.timeRemaining()
: 返回一个DOMHighResTimeStamp
,其为浮点数,用来表示当前闲置周期的预估剩余毫秒数。如果闲置期已经结束,则其值为0
。你的回调函数可以重复调用该函数,以判断目前是否有足够的时间来执行更多的任务。
-
-
options
: 包括可选的配置参数。具有如下属性:timeout
: 果指定了timeout
,并且有一个正值,而回调在timeout
毫秒过后还没有被调用,那么回调任务将放入事件循环中排队,即使这样做有可能对性能产生负面影响。手动设置timeout
参数可能会导致 app 无响应, 影响用户体验, 所以尽量让浏览器自行来计算决定.
-
callbackID
: 一个 ID,可以把它传入Window.cancelIdleCallback()
方法来结束回调。
三、用法
3.1 空闲时间
class Scheduler {
constructor() {
this.tasks = [];
this.callbackID = null;
}
add(task) {
this.tasks.push(task);
if (!this.callbackID) {
this.start();
}
}
start() {
this.callbackID = window.requestIdleCallback((deadline) => {
while ((deadline.timeRemaining() > 0) && this.tasks.length > 0) {
const task = this.tasks.shift();
task();
}
if (this.tasks.length > 0) {
this.start();
} else {
window.cancelIdleCallback(this.callbackID);
this.callbackID = null;
}
});
}
}
3.2 任务超时
class Scheduler {
constructor() {
this.tasks = [];
this.callbackID = null;
}
add(task) {
this.tasks.push(task);
if (!this.callbackID) {
this.start();
}
}
start() {
this.callbackID = window.requestIdleCallback((deadline) => {
while ((deadline.didTimeout) && this.tasks.length > 0) {
const task = this.tasks.shift();
task();
}
if (this.tasks.length > 0) {
this.start();
} else {
window.cancelIdleCallback(this.callbackID);
this.callbackID = null;
}
});
}
}
3.3 空闲时间或任务超时
class Scheduler {
constructor() {
this.tasks = [];
this.callbackID = null;
}
add(task) {
this.tasks.push(task);
if (!this.callbackID) {
this.start();
}
}
start() {
this.callbackID = window.requestIdleCallback((deadline) => {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && this.tasks.length > 0) {
const task = this.tasks.shift();
task();
}
if (this.tasks.length > 0) {
this.start();
} else {
window.cancelIdleCallback(this.callbackID);
this.callbackID = null;
}
});
}
}