浏览器requestIdleCallback的原理和应用场景

一般的浏览器每秒钟会绘制60帧,也就是每帧需要16ms左右,如果js计算任务长时间占用线程那个,会导致一些ui无法及时得到渲染,出现卡顿。

一帧内需要完成如下六个步骤的任务:

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

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

语法:

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

 

实例:下面的例子知识模拟情况对比,实际情况下尽量避免在requestIdlecallback中操作dom.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>requestIdelCallback</title>
</head>
<body>
      <div id ="ui">0</div>
      <div>执行<span id="test">0</span></div>
      <button onclick = "mytest()">常规执行计算任务</button>
      <button onclick = "startRequestIdleCallback()">在requestIdleCallback中执行计算任务</button>
<script>
    // 任务队列
    let task1 = [
        function(){
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第一个任务");
            document.getElementById('test').innerHTML = 1
        },
        function(){  
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第二个任务");
            document.getElementById('test').innerHTML = 2
        },
        function(){
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第三个任务");
            document.getElementById('test').innerHTML = 3
        },
    ];
    let task2 = [
        function(){
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第一个任务");
            document.getElementById('test').innerHTML = 1
        },
        function(){  
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第二个任务");
            document.getElementById('test').innerHTML = 2
        },
        function(){
            for(var i=0;i<3000;i++){
                console.log(i)
            }
            console.log("第三个任务");
            document.getElementById('test').innerHTML = 3
        },
    ];
    setInterval(() => {
        document.getElementById('ui').innerHTML = parseInt(document.getElementById('ui').innerHTML) + 1
    },100)
    //执行mytest文件会影响上面动态dom的渲染
    function mytest(){
        if (task1.length > 0){
            work1(task1)
        }
    }
    
    //将js计算任务放在requestIdleCallback中运算
    function startRequestIdleCallback(){
        requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
    }
    
    function myNonEssentialWork (deadline) {
        //alert(deadline.timeRemaining())
        console.log(deadline,deadline.timeRemaining())
      // 如果帧内有富余的时间,或者超时
      while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && task2.length > 0) {
        work2(task2);
      }
      //开启下一个空闲时间的回调函数
      if (task2.length > 0){requestIdleCallback(myNonEssentialWork);}
    }
    // ​
    function work1 (tasks) {
      tasks.shift()();
      console.log('执行任务');
      if(task1.length > 0){
        work1(task1)
        
      }
      
    }
    function work2 (tasks) {
      tasks.shift()();
      console.log('执行任务');
    }
    
</script>
</body>
</html>

通过上面的例子可以得到:

1,直接执行任务队列,会导致#test元素的文本无法及时渲染且 定时渲染任务会出现长时间卡顿

2,在requestIdleCallback中执行任务队列,是在空闲时间去执行队列中的任务,#test的文本内容可以得到及时渲染并且定时任务卡顿不那么严重

3,虽然requestIdleCallback是在空闲时间去执行队列中的任务,但如果某个回调任务的计算耗时过长,也会导致当前帧的延长,出现卡顿

4,所以要把任务分成更小的任务单元,在浏览器空余时间执行,多次往返循环

总结

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

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

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

参考:网页渲染性能优化 —— 性能优化上

           https://www.jianshu.com/p/2771cb695c81

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值