current position:Home>Dry goods | RAF replaces setTimeout_ setInterval

Dry goods | RAF replaces setTimeout_ setInterval

2021-08-27 10:20:20 Weibo front end

Preface

Why requestAnimationFrame Instead of setInterval and setTimeout? You have to understand the micro task first 、 Macro task and Event-Loop, also setInterval The problem is

requestAnimationFrame

window.requestAnimationFrame() Tell the browser —— You want to perform an animation ,

It also requires the browser to invoke the specified callback function before updating the animation next time . This method needs to pass in a callback function as a parameter , This callback function will execute before the next redraw of the browser

If you want to update the next frame of animation before the browser redraw next time , Then the callback function itself must call... Again window.requestAnimationFrame()

grammar

window.requestAnimationFrame(callback);

  • callback The function called to update the animation frame before the next redraw ( That is the callback function mentioned above ). The callback function will be passed Enter into DOMHighResTimeStamp Parameters , The parameter and performance.now() The return value of is the same , It said requestAnimationFrame() open The moment when the callback function starts to execute .
  • Return value One long Integers , request ID , Is the unique identifier in the callback list . It's a non-zero value , Nothing else . You can pass this value to window.cancelAnimationFrame() To cancel the callback function .

Understand micro tasks 、 Macro task and Event-Loop

Micro task _ Macro task and Event-Loop

Operating mechanism

setTimeout

setTimeout(function(){
    console.log(1);
});
console.log(0);

 Copy code 

   actually , hold setTimeout The second parameter of is set to 0s, It doesn't mean to execute the function immediately , Just put the function on the asynchronous queue . The browser first executes the tasks in the synchronization queue , Will execute the tasks in the asynchronous queue

   In the following example , Give me a button btn Set up an event handler . The event handler sets a 250ms The timer after invoking . After clicking this button , First of all, will onclick Event handler joins the queue . The timer is set after the program is executed , There's more 250ms after , The specified code is added to the queue for execution


btn.onclick = function(){
    setTimeout(function(){
        console.log(1);
    },250);
}
 Copy code 

   If... In the code above onclick The event handler executed 300ms, Then the timer code should be at least after the timer is set 300ms And then it's executed . All in the queue Your code has to wait until javascript The process cannot be executed until it is idle , And no matter how they are added to the queue

setInterval

Use setInterval() The problem is , The timer code may not have finished executing before the code is added to the queue again , The result is that the timer code runs several times in a row , And there was no pause . and javascript The engine's solution to this problem is : When using setInterval() when , Only if there is no other code instance of the timer , To add the timer code to the queue . This ensures that the timer code The minimum time interval for adding to the queue is the specified interval

   however , This leads to two problems :1、 Some intervals are skipped ;2、 The interval between code execution of multiple timers may be smaller than expected

   hypothesis , Some onclick Used by event handlers setInterval() Set up 200ms Interval timer . If the event handler spends 300ms A little more time to finish , At the same time, the timer code also took a lot of time More time , There will also be cases of skipping an interval

setinterval.jpg    The first timer in the example is in 205ms Added to the queue at , But until after 300ms Only when we can carry out . When this timer code is executed , stay 405ms And then to the queue Added another copy . At the next interval , namely 605ms It's about , The first timer code is still running , At the same time, there is already a timing in the queue An example of the handler code . The result is , The timer code at this point in time will not be added to the queue

iteration setTimeout Realization setInterval

Some companies will disable setInterval for fear of setInterval() Timer problem , You can use chain setTimeout() call

setTimeout(function fn(){
    setTimeout(fn,interval);
},interval);
 Copy code 

simulation setInterval/setTimeout Basic use of

RAF

class RAF {
  constructor () {
    this.init()
  }
  init () {
    this._timerIdMap = {
      timeout: {},
      interval: {}
    }
  }
  run (type = 'interval', cb, interval = 16.7) {
    const now = Date.now
    let stime = now()
    let etime = stime
    // establish Symbol Type as key value , Ensure the uniqueness of the returned value , Used to clear the timer 
    const timerSymbol = Symbol()
    const loop = () => {
      this.setIdMap(timerSymbol, type, loop)
      etime = now()
      if (etime - stime >= interval) {
        if (type === 'interval') {
          stime = now()
          etime = stime
        }
        cb()
        type === 'timeout' && this.clearTimeout(timerSymbol)
      }
    }
    this.setIdMap(timerSymbol, type, loop)
    return timerSymbol //  return Symbol Make sure that every time you call setTimeout/setInterval The uniqueness of the return value 
  }
  setIdMap (timerSymbol, type, loop) {
    const id = requestAnimationFrame(loop)
    this._timerIdMap[type][timerSymbol]= id
  }
  setTimeout (cb, interval) {  //  Realization setTimeout  function 
    return this.run('timeout', cb, interval)
  }
  clearTimeout (timer) {
    cancelAnimationFrame(this._timerIdMap.timeout[timer])
  }
  setInterval (cb, interval) { //  Realization setInterval function 
    return this.run('interval', cb, interval)
  }
  clearInterval (timer) {
    cancelAnimationFrame(this._timerIdMap.interval[timer])
  }
}
 Copy code 

Use

var raf = new RAF()
var timer1 = raf.setInterval(() =>{
  console.log(1000)
}, 1000)

var timer2 = raf.setInterval(() =>{
  console.log(1500)
}, 1500)

raf.setTimeout(() => {
  raf.clearInterval(timer1)
  raf.clearInterval(timer2)
}, 6000)
 Copy code 

Calculate the computer refresh rate

 //  Calculate out of the question  requestAnimationFrame  refresh frequency 
  let startTime = Date.now()
  let timer = null
  let arr = []
  function calc () {
    timer = requestAnimationFrame(calc)
    const nowTime = Date.now()
    const diff = nowTime - startTime
    startTime = nowTime
    arr.push(diff)
  }
  calc()
  setTimeout(() => {
    const newArr = arr.filter(v => v > 10)
    console.log(newArr)
    const total = newArr.reduce((t, i) => t + i, 0)
    console.log(' Average refresh times ', 1000 / (total / newArr.length)) //  Average refresh times  60.00670465973852
    cancelAnimationFrame(timer)
  }, 3000)
 Copy code 

quote

more

weibozzz.github.io/

copyright notice
author[Weibo front end],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827102017506n.html

Random recommended