requestIdleCallback 适合做什么?和 requestAnimationFrame 有什么区别?
简化版
requestAnimationFrame 适合做下一帧前必须执行的视觉更新,例如动画和测量绘制;requestIdleCallback 适合做低优先级、可延后的后台任务,例如预计算、非关键埋点、缓存整理和预加载决策。它们的区别是:rAF 贴近渲染帧,idle callback 利用主线程空闲时间。低优先级任务要拆小块,并设置超时兜底。
详细版
浏览器主线程要处理输入、JS、渲染和网络回调。任务调度的目标是不要让非关键工作抢占用户交互和渲染。
| API | 执行时机 | 适合任务 |
|---|---|---|
requestAnimationFrame | 下一次绘制前 | 动画、视觉更新 |
requestIdleCallback | 浏览器空闲时 | 低优先级后台任务 |
setTimeout | 定时器队列 | 简单延迟 |
queueMicrotask | 当前任务后立刻执行 | 状态收尾、Promise 后续 |
requestIdleCallback(deadline => {
while (deadline.timeRemaining() > 0 && tasks.length) {
runSmallTask(tasks.shift())
}
}, { timeout: 2000 })
调度优化的关键是让重要任务先执行,让可延后的任务别挤占渲染和交互。
完整版教学
一、为什么需要调度
前端页面里有很多任务:渲染 UI、响应点击、上报埋点、预加载数据、格式化大列表、写缓存。如果所有任务都同步执行,主线程会被占满。
调度的核心是区分优先级。用户正在点击和滚动时,交互响应更重要;后台统计和缓存整理可以晚一点。
二、requestAnimationFrame 的定位
requestAnimationFrame 会在浏览器下一次绘制前执行回调。它适合做和视觉帧同步的任务。
function animate() {
box.style.transform = `translateX(${x}px)`
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
它不适合放大量非视觉计算,否则会挤占每帧 16.7ms 的预算。
三、requestIdleCallback 的定位
requestIdleCallback 在浏览器空闲时执行。回调参数里有 timeRemaining(),可以知道当前空闲时间大概还剩多少。
它适合:
- 非关键埋点聚合。
- 缓存清理。
- 搜索索引预构建。
- 非首屏数据预处理。
- 预加载候选资源计算。
四、任务必须切小
idle callback 不是让你把大任务直接塞进去。如果任务一次跑 500ms,仍然会阻塞主线程。
正确做法是切成小块,每次只在剩余时间内处理一部分。
function scheduleWork() {
requestIdleCallback(deadline => {
while (deadline.timeRemaining() > 1 && queue.length) {
queue.shift()?.()
}
if (queue.length) scheduleWork()
}, { timeout: 1000 })
}
五、timeout 的意义
如果页面一直很忙,idle callback 可能很久不执行。timeout 可以设置最长等待时间,保证任务不会无限延后。
但 timeout 到了后可能在繁忙时执行,所以回调内仍要控制任务量。
六、兼容性和替代方案
requestIdleCallback 并非所有环境都一致支持。项目里可以做降级,用 setTimeout 模拟低优先级调度。
const idle = window.requestIdleCallback ?? ((cb: any) => setTimeout(() => cb({
timeRemaining: () => 0
}), 1))
复杂应用还可以使用调度库或框架内置调度机制。
七、常见误区与追问
- 误区:idle callback 里的任务不会卡页面。 如果任务本身太大,仍然会占用主线程。
- 误区:rAF 适合所有异步任务。 rAF 应优先服务视觉更新,非关键后台任务不应抢帧。
- 误区:idle callback 一定会马上执行。 页面忙时可能延迟很久,需要 timeout 兜底。
- 追问:埋点适合放 idle 吗? 非关键埋点可以,但关键转化事件要及时可靠上报。
- 追问:大计算用 idle 还是 Worker? 大计算更适合 Worker,idle 适合轻量可切片任务。
- 追问:为什么任务要切片? 切片能把长任务拆成短任务,减少主线程连续阻塞。
八、加强记忆
rAF 记成“跟着下一帧画”,idle callback 记成“空了再做”。视觉任务走 rAF,低优先级后台任务走 idle,大任务要切片,重计算要考虑 Worker。