requestidlecallback 的核心是合理调度非关键任务:需设 timeout 防止不执行,按时间片拆分任务并检查 deadline.timeremaining() > 1.5ms,避免触发重排、前台保守执行、后台加速,并备 web worker 等兜底方案。

用 requestIdleCallback 执行非核心逻辑,关键不是“能不能调用”,而是“什么时候调、调多久、怎么退”。它本身不保证 60FPS,但配合合理策略,能避开主线程繁忙时段,让动画帧有足够时间渲染。
理解 requestIdleCallback 的真实行为
它只在浏览器空闲时触发,且每次最多执行约 50ms(实际取决于帧预算)。如果当前帧只剩 10ms,回调可能被截断或延迟到下一空闲期。它不按优先级排队,也不支持取消——一旦注册,只能等执行或超时。
- 务必传入
{ timeout: 3000 },防止任务永远不执行(比如页面切到后台后又切回) - 不要假设每次都能跑完全部逻辑:需自行拆分任务、保存进度、可中断重入
- 它和
setTimeout(fn, 0)完全不同——后者会抢占帧末尾,可能挤占渲染时间;而requestIdleCallback主动让出,更“守规矩”
把长任务切成微任务块,并检查帧余量
别在一个 idle 回调里遍历 10 万条数据。应按时间片执行,每轮做完立刻判断是否该停。
- 用
deadline.timeRemaining() > 1.5作为继续条件(留出约 1.5ms 缓冲,避免卡住下一帧) - 维护一个待处理队列(如数组或迭代器),每次只处理几项,记录已处理索引
- 示例逻辑:处理一批 DOM 更新 → 检查剩余时间 → 还够?继续;不够?存状态,下个 idle 再来
避开动画关键路径,主动降级或跳过
即使用了 idle,若逻辑间接影响样式计算或布局(比如读取 offsetHeight),仍可能触发强制同步布局,拖慢动画。
- 避免在 idle 回调中访问任何会触发重排/重绘的属性(
offsetTop、getBoundingClientRect()等) - 对非实时性要求高的逻辑(如日志上报、缓存预热),可在
document.hidden === true时加速执行,前台则更保守 - 监听
document.visibilityState或pagehide,及时清理未完成的 idle 任务
兜底方案:空闲不足时改走 Web Worker 或节流
当页面持续高负载(如滚动+动画+大量计算),idle 时间可能长期为 0。此时硬等 idle 反而导致逻辑积压失效。
- 设置 fallback 计数器:连续 3 次
timeRemaining() ,就改用 <code>setTimeout(fn, 100)强制推进一小步 - 可将纯计算类任务(如 JSON 解析、数组排序)直接移交 Web Worker,完全不占主线程
- 对 UI 相关但非紧急的操作(如非焦点输入框的防抖校验),可用
requestAnimationFrame+ 节流组合,确保在帧内完成而非抢帧











