requestIdleCallback 是浏览器在主线程空闲时执行低优先级任务的 API,适用于日志上报、DOM 清理等非紧急操作,不保证执行时机,支持超时机制,需兼容 Safari 降级处理。

requestIdleCallback 是浏览器提供的 API,用于在主线程空闲时(即没有高优先级任务如渲染、用户输入响应等需要处理时)执行低优先级任务,避免阻塞页面交互或动画,特别适合处理长耗时但不紧急的逻辑(如日志上报、非关键数据预处理、DOM 清理等)。
基本用法:注册空闲回调
调用 requestIdleCallback 传入一个回调函数,浏览器会在下一个空闲时段调度执行它,并传入一个 IdleDeadline 对象:
const handleIdle = (deadline) => {
// deadline.timeRemaining() 返回剩余可用空闲时间(毫秒),通常约 1–50ms
// deadline.didTimeout 表示是否因超时(如等待太久未执行)而被强制触发
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
processNextTask();
}
// 若还有任务未完成,且未超时,可递归调度下一次空闲回调
if (tasks.length > 0 && !deadline.didTimeout) {
requestIdleCallback(handleIdle);
} else if (tasks.length > 0) {
// 超时了,说明空闲时间不足,可降级为 setTimeout 延后执行(避免卡死)
setTimeout(() => {
processAllRemainingTasks();
}, 0);
}
};
requestIdleCallback(handleIdle);
关键细节与注意事项
- 不是定时器:它不保证执行时机或频率,只在浏览器判定“空闲”时才触发;页面繁忙(如滚动、动画、JS 执行中)时可能长时间不执行。
-
有超时机制:可传入
{ timeout: 2000 }选项,强制在 2 秒内执行(即使未空闲),防止任务无限积压。此时deadline.didTimeout为true。 -
不支持 Promise / await 直接等待:回调是同步执行的,若需异步操作(如
fetch),应在空闲回调中发起,但不要 await 阻塞后续空闲检查。 -
兼容性需注意:Chrome 47+、Edge 16+、Firefox 55+ 支持;Safari 未实现(截至 Safari 17.5),需降级方案(如
setTimeout(..., 0)或结合document.visibilityState判断页面是否隐藏后延后执行)。
实用封装建议
为提升复用性,可简单封装一个支持取消、自动续调、降级的空闲任务队列:
class IdleScheduler {
constructor({ timeout = 2000 } = {}) {
this.timeout = timeout;
this.pending = new Set();
}
schedule(task) {
const run = (deadline) => {
const start = performance.now();
while (deadline.timeRemaining() > 0 && this.pending.size > 0) {
const fn = this.pending.values().next().value;
fn();
this.pending.delete(fn);
}
if (this.pending.size > 0) {
if (deadline.didTimeout) {
// 超时,切到微任务队列继续
Promise.resolve().then(() => run({ timeRemaining: () => 10 }));
} else {
requestIdleCallback(run, { timeout: this.timeout });
}
}
};
this.pending.add(task);
requestIdleCallback(run, { timeout: this.timeout });
}
clear() {
this.pending.clear();
}
}
// 使用
const scheduler = new IdleScheduler({ timeout: 3000 });
scheduler.schedule(() => console.log('清理缓存'));
scheduler.schedule(() => sendAnalyticsBeacon());
什么任务适合?什么不适合?
- 适合:非实时数据聚合、离线缓存更新、非关键 DOM 批量修改(如隐藏元素的懒移除)、性能监控采样、大数组分片处理。
-
不适合:依赖及时响应的操作(如用户点击后的 UI 反馈)、动画帧内逻辑(应使用
requestAnimationFrame)、必须在页面卸载前完成的任务(beforeunload中无法可靠触发空闲回调)。


















