你了解 requestIdleCallback 的工作原理和应用场景吗?

requestIdleCallback 是一种浏览器 API,用于在主线程空闲时执行低优先级任务。它适用于非关键性的日志上报、资源预加载和分片处理大型任务。

异步编程 中等 性能优化 API 浏览器API

requestIdleCallback 是浏览器提供的用于在主线程空闲时执行低优先级任务的 API。它通过合理调度非关键任务(如日志上报、数据预取等),避免阻塞页面关键渲染和用户交互,从而提升性能体验。

一、核心原理

浏览器每帧(约 16ms)的任务流程包括:JS 执行 → 样式计算 → 布局 → 绘制 → 合成。若一帧任务提前完成(如耗时 10ms),剩余时间(6ms)即空闲时段。此时满足以下条件会触发回调:

  • 主线程无高优先级任务(如动画、输入响应);
  • 当前帧有空闲时间;
  • 开发者可选设置超时时间(timeout)强制触发回调。

回调函数接收 IdleDeadline 参数,包含两个关键属性:

  • timeRemaining():返回当前帧剩余空闲时间(单位毫秒,通常 ≤50ms);
  • didTimeout:布尔值,标记回调是否因超时被强制触发。

语法示例:

const taskId = requestIdleCallback(callback, { timeout: 2000 });

function callback(deadline) {
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    executeTask(tasks.shift()); // 执行单个任务
  }
  if (tasks.length > 0) {
    requestIdleCallback(callback); // 继续调度剩余任务
  }
}
// 取消任务:cancelIdleCallback(taskId);

二、适用场景

  1. 非关键日志上报/数据统计
    用户行为埋点等数据可延迟上报,避免占用关键渲染时间。
  2. 资源预加载
    提前加载下一页数据或资源(如懒加载后续内容),不阻塞当前交互。
  3. 分片大型任务
    将复杂任务(如大数据处理)拆分为小模块,逐块执行以防界面卡顿:
    function processChunk(deadline) {
      while (deadline.timeRemaining() > 0 && dataChunks.length) {
        renderChunk(dataChunks.pop());
      }
      if (dataChunks.length) requestIdleCallback(processChunk);
    }
    

    相比 setTimeout,此方法精准利用空闲时段执行分片任务,避免抢占主线程。

  4. 非渲染相关 DOM 变更
    对隐藏元素(如 Offscreen Canvas)的修改,不影响当前布局与绘制。

三、注意事项

  • 超时设置:通过 options.timeout 避免长时未调用(如设定 2 秒强制执行)。
  • 执行顺序:超时可能打乱回调队列的先进先出顺序。
  • 兼容性与替代:React 未直接采用此 API(兼容性及帧率限制),而是通过 MessageChannel + requestAnimationFrame 模拟实现。