javascript探秘之requestIdleCallback

180 阅读2分钟

前言

在网页中,有许多耗时但是却又不能那么紧要的任务。它们和紧要的任务,比如对用户的输入作出及时响应的之类的任务,它们共享事件队列。如果两者发生冲突,用户体验会很糟糕。我们可以使用setTimout,对这些任务进行延迟处理。但是我们并不知道,setTimeout在执行回调时,是否是浏览器空闲的时候。而requestIdleCallback就解决了这个痛点,requestIdleCallback会在帧结束时并且有空闲时间,或者用户不与网页交互时,执行回调。

帧渲染过程

微信图片_20220307191450.png 在每一帧的渲染过程当中都会有一个空闲时间段,将我们的耗时任务插入其中来进行处理,不会影响之前的渲染进度,从而达到流畅度的提升。帧时间的计算方式是1000ms/刷新率 = 一帧的时间。比如1000ms/60hz 约等于16.6左右。

应用场景

requestIdleCallback实现页面快速响应 实例代码:

    let b = document.getElementById("move")
    let c = document.getElementById("c")
    let begin = 1000
    let width=100
    function move(){
      begin=100000
      while(begin>=0){
        console.log(1)
        begin--
      }
    }
    b.addEventListener("click",()=>{
        move()//如果我们直接执行的话,浏览器会卡住,move方法是通过主线程进行的渲染的
        //requestIdleCallback(move) //当我们用这个方法执行的时候,此时卡顿效果就会明显的减弱。
        c.style.width=width+100+"px"
        width++
    })
    

再来一个例子感受一下requestIdleCallback的威力

    let c = document.getElementById("c")
    //睡眠方法
    function delay(d){
      let t=Date.now()
      for(let i=Date.now();Date.now()-i<=d;){}
    }
    //任务列表
    let works=[
      ()=>{
        delay(20)
        console.log(1,"being")
        c.innerHTML="1"
        c.style.width="200px"
        console.log(1,"end")
      },
      ()=>{
        console.log(2,"being")
        c.innerHTML="2"
        c.style.width="300px"
        delay(20)
        console.log(2,"end")
      },
      ()=>{
        console.log(3,"being")
        c.innerHTML="3"
        c.style.width="100px"
        delay(20)
        console.log(3,"end")
      }
    ]
    //调度方法
    function dod(deadline){
        while(deadline.timeRemaining()>0&&works.length>0){
          let work= works.shift()
          work()
        }
        if(works.length>0){
          maindo()
          requestIdleCallback(dod)
        }
      }
    function maindo(){
      console.log("领导来了,先处理")
    }
    //开始执行
    requestIdleCallback(dod)

假设一个场景 我们有一些列的子任务需要处理,但是又不能来阻塞主线程的任务,但是又要尽快执行子任务,此时就可用这个方式来执行了,虽然promise也可以做到,是根据js事件队列的处理模型来看,又无法满足我们的要求(当微任务过多的时候就会又长时间的延迟)

函数使用说明

//var handle = window.requestIdleCallback(callback[, options])
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
  • callback:回调,即空闲时需要执行的任务,该回调函数接收一个IdleDeadline对象作为入参。其中IdleDeadline对象包含:

    • didTimeout,布尔值,表示任务是否超时,结合 timeRemaining 使用。
    • timeRemaining(),表示当前帧剩余的时间,也可理解为留给任务的时间还有多少。
  • options:目前 options 只有一个参数

    • timeout。表示超过这个时间后,如果任务还没执行,则强制执行,不必等待空闲。