• 利用好浏览器的空闲时间 --- requestIdleCallback


    页面流畅与 FPS

    页面是一帧一帧绘制出来的,当每秒绘制的帧数(FPS)达到 60 时,页面是流畅的,小于这个值时,用户会感觉到卡顿。

    1s 60帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms。所以我们书写代码时力求不让一帧的工作量超过 16ms。

    Frame

    那么浏览器每一帧都需要完成哪些工作?

    image
    浏览器一帧内的工作

    通过上图可看到,一帧内需要完成如下六个步骤的任务:

    • 处理用户的交互
    • JS 解析执行
    • 帧开始。窗口尺寸变更,页面滚去等的处理
    • rAF
    • 布局
    • 绘制

    requestIdleCallback

    上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行 requestIdleCallback 里注册的任务。

    image
    requestIdleCallback 在浏览器一帧内的位置示意

    从上图也可看出,和 requestAnimationFrame 每一帧必定会执行不同,requestIdleCallback 是捡浏览器空闲来执行任务。

    如此一来,假如浏览器一直处于非常忙碌的状态,requestIdleCallback 注册的任务有可能永远不会执行。此时可通过设置 timeout (见下面 API 介绍)来保证执行。

    API

    var handle = window.requestIdleCallback(callback[, options])
    • callback: ():回调即空闲时需要执行的任务,接收一个 IdleDeadline 对象作为入参。其中 IdleDeadline 对象包含:
      • didTimeout,布尔值,表示任务是否超时,结合 timeRemaining 使用。
      • timeRemaining(),表示当前帧剩余的时间,也可理解为留给任务的时间还有多少。
    • options:目前 options 只有一个参数
      • timeout 。表示超过这个时间后,如果任务还没执行,则强制执行,不必等待空闲。

    示例

    requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
    

    function myNonEssentialWork (deadline) {

      // 如果帧内有富余的时间,或者超时
      while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
             tasks.length > 0)
        doWorkIfNeeded();

      if (tasks.length > 0)
        requestIdleCallback(myNonEssentialWork);
    }

    超时的情况,其实就是浏览器很忙,没有空闲时间,此时会等待指定的 timeout 那么久再执行,通过入参 dealine 拿到的 didTmieout 会为 true,同时 timeRemaining () 返回的也是 0。超时的情况下如果选择继续执行的话,肯定会出现卡顿的,因为必然会将一帧的时间拉长。

    cancelIdleCallback

    setTimeout 类似,返回一个唯一 id,可通过 cancelIdleCallback 来取消任务。

    总结

    一些低优先级的任务可使用 requestIdleCallback 等浏览器不忙的时候来执行,同时因为时间有限,它所执行的任务应该尽量是能够量化,细分的微任务(micro task)。

    因为它发生在一帧的最后,此时页面布局已经完成,所以不建议在 requestIdleCallback 里再操作 DOM,这样会导致页面再次重绘。DOM 操作建议在 rAF 中进行。同时,操作 DOM 所需要的耗时是不确定的,因为会导致重新计算布局和视图的绘制,所以这类操作不具备可预测性。

    Promise 也不建议在这里面进行,因为 Promise 的回调属性 Event loop 中优先级较高的一种微任务,会在 requestIdleCallback 结束时立即执行,不管此时是否还有富余的时间,这样有很大可能会让一帧超过 16 ms。

    参考

  • 相关阅读:
    vbs实现c++的vector
    二叉树的线索化及其遍历(必会)
    二叉树的7种遍历(必背)
    PyQt5发布技巧:指定插件(plugins)路径
    pyqt5环境变量踩坑记
    怎样把ndarray转换为PyQt中的QPixmap
    桌面应用“标注器”打包踩坑记
    怎样用cmd脚本添加Qt的环境变量
    Krpano动态热点添加始终显示的文字
    VS解决方案中个文件类型含义
  • 原文地址:https://www.cnblogs.com/Wayou/p/requestIdleCallback.html
Copyright © 2020-2023  润新知