requestIdleCallback 与 requestAnimationFrame
两种浏览器调度 API 的区别与使用场景
问题
requestIdleCallback 和 requestAnimationFrame 有什么区别?分别在什么场景下使用?
解答
requestAnimationFrame
在浏览器下一次重绘之前执行回调,通常每秒 60 次(约 16.67ms 一次)。
// 用于流畅动画
function animate() {
// 更新动画状态
element.style.transform = `translateX(${position}px)`;
position += 2;
if (position < 300) {
// 继续下一帧
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
requestIdleCallback
在浏览器空闲时执行回调,适合低优先级任务。
// 用于非紧急任务
requestIdleCallback((deadline) => {
// deadline.timeRemaining() 返回当前帧剩余时间(ms)
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
// 执行任务
const task = tasks.shift();
task();
}
// 还有任务,继续排队
if (tasks.length > 0) {
requestIdleCallback(processTask);
}
}, { timeout: 2000 }); // 最多等待 2 秒
执行时机对比
一帧的生命周期(约 16.67ms):
输入事件 → JS 执行 → rAF 回调 → 样式计算 → 布局 → 绘制 → [空闲时间] → rIC 回调
↑ ↑
requestAnimationFrame requestIdleCallback
完整示例
// 动画任务 - 使用 rAF
function smoothScroll(targetY) {
const startY = window.scrollY;
const distance = targetY - startY;
let startTime = null;
function step(timestamp) {
if (!startTime) startTime = timestamp;
const progress = Math.min((timestamp - startTime) / 500, 1);
window.scrollTo(0, startY + distance * easeOutCubic(progress));
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
// 非紧急任务 - 使用 rIC
function sendAnalytics(data) {
requestIdleCallback(() => {
// 上报埋点数据,不影响用户交互
fetch('/analytics', {
method: 'POST',
body: JSON.stringify(data)
});
}, { timeout: 5000 });
}
// 分片处理大量数据 - 使用 rIC
function processLargeArray(items, callback) {
const queue = [...items];
function process(deadline) {
while (deadline.timeRemaining() > 0 && queue.length > 0) {
callback(queue.shift());
}
if (queue.length > 0) {
requestIdleCallback(process);
}
}
requestIdleCallback(process);
}
兼容性处理
// requestIdleCallback polyfill
window.requestIdleCallback = window.requestIdleCallback || function(cb) {
const start = Date.now();
return setTimeout(() => {
cb({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
});
}, 1);
};
window.cancelIdleCallback = window.cancelIdleCallback || function(id) {
clearTimeout(id);
};
关键点
- 执行时机:rAF 在重绘前执行,rIC 在空闲时执行
- 调用频率:rAF 每帧必调用(约 60fps),rIC 可能被延迟或跳过
- 使用场景:rAF 用于动画和 DOM 操作,rIC 用于埋点、预加载等低优先级任务
- API 差异:rIC 回调接收 deadline 对象,可查询剩余时间;支持 timeout 选项强制执行
- 兼容性:rAF 支持良好,rIC 在 Safari 中不支持,需要 polyfill
目录