浏览器 requestIdleCallback 方法

410 阅读2分钟

浏览器 requestIdleCallback 方法

我们都知道,页面的内容都是一帧一帧绘制出来的,浏览器刷新率代表浏览器一秒绘制多少帧。原则上说 1s 内绘制的帧数也多,画面表现就也细腻。
目前浏览器大多是 60Hz(60 帧/s),每一帧耗时也就是在 16.6ms 左右。那么在这一帧的(16.6ms) 过程中浏览器又干了些什么呢?

image.png 浏览器一帧会经过下面这几个过程:

  1. 接受输入事件.(处理用户的交互,如点击、触碰、滚动等事件)
  2. 执行事件回调
  3. 开始一帧
  4. 执行 RAF (RequestAnimationFrame)
  5. 页面布局,样式计算
  6. 绘制渲染
  7. 执行 RIC (RequestIdelCallback)

其实 RIC 并不是浏览器每帧都会去执行。而是在浏览器一帧(16.6ms)中做完了前面 6 件事儿且还有剩余时间,才会执行,如果没有剩余时间,那么就不会去执行了。
如果一帧执行结束后还有时间执行 RIC 事件,那么下一帧需要在事件执行结束才能继续渲染,所以 RIC 执行不要超过 30ms,如果长时间不将控制权交还给浏览器,会影响下一帧的渲染,导致页面出现卡顿和事件响应不及时 重点来了。我们怎么知道浏览器是否还有一帧剩余时间呢?
请看RequestIdelCallback 函数入参 deadline

requestIdleCallback((deadline) => {
    // deadline 有两个参数 
    // timeRemaining(): 当前帧还剩下多少时间,最大值50ms 
    // didTimeout: 是否超时 
    // 另外 requestIdleCallback 后如果跟上第二个参数 {timeout: ...} 则会强制浏览器在当前帧执行完后执行。 
    if (deadline.timeRemaining() > 0) { 
        // TODO 
    } else { 
        requestIdleCallback(otherTasks); 
    } 
});

// 用法示例
let tasksNum = 10000;

requestIdleCallback(unImportWork);

function unImportWork(deadline) {
  while (deadline.timeRemaining() && tasksNum > 0) {
    console.log(`执行了${10000 - tasksNum + 1}个任务`);
    tasksNum--;
  }
  if (tasksNum > 0) {
    // 在未来的帧中继续执行
    requestIdleCallback(unImportWork);
  }
}

这个API因浏览器版本兼容问题。Facebook 在 React 的重构升级中, 抛弃了 requestIdleCallback 的原生 API,而实现了功能更完备的 requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler 还提供了多种调度优先级供任务设置: