SetInterval quasi?

You can only ensure that the callback function does not run before the specified event interval, but may run at that time or after, depending on the state of the event queue. JavaScript you Don’t Know p143

Use setTimeout instead of setInterval

  • Why substitute?

SetInterval will wait for the call stack to be empty before executing the callback. If the previous code has been executed for too long, beyond the interval set for setInterval, the callback function will be queued. When the stack is finally empty, the callback function in the queue will be executed immediately. SetInterval in the Web APIs has been timing the callback function for a while, and will soon queue the callback function again. In addition, if setInterval itself gives a callback that takes longer than the setInterval, it will also cause this problem, losing the value of setting the interval.

  • How to replace?
Train of thought

You can refer to this video, the idea is very detailed, JavaScript using setTimeout simulation to implement setInterval, but the video does not say how to clear the timer, the following record the thought process of implementing the clear timer.

Use: timer = mySetInterval(fn, delay) Clear: myClearInterval(timer)

// Method 1 (this is not good to clear the timer, because it is not good to set the global timer, the video does not talk about this method)
function mySetInterval(fn, delay) {
    setTimeout(() = > {
        console.log(new Date().toLocaleString()) // You can print the time to see
        fn()
        mySetInterval(fn, delay)
    }, delay)
}

/ / test
function testmySetInterval() {
    console.log('testmySetInterval')
}

mySetInterval(testmySetInterval, 1000)
Copy the code

Method one is not good to clear the timer, because mySetInterval must return a timer to clear it, right? Let’s do method two.

// Method 2 (video method, no write how to clear timer)
function mySetInterval(fn, delay) {
    function inside() {
        console.log(new Date().toLocaleString()) // You can print the time to see
        fn()
        setTimeout(inside, delay)
    }
    setTimeout(inside, delay)
}

// Start thinking:
MySetInterval (fn, delay) returns a timer (fn, delay).
function mySetInterval(fn, delay) {
    let timer = null
    function inside() {
        clearTimeout(timer)
        fn()
        timer = setTimeout(inside, delay)
    }
    timer = setTimeout(inside, delay)
    return timer  // Timer = mySetInterval(fn, delay
}

// mySetInterval is called only once, which always returns the first setTimeout timer. How do I make the timer unfixed? Object! Return an object clearTimeout() as the property value! The property value is a method that clears the timer, which myClearInterval calls! Write like this:
function mySetInterval(fn, delay) {
    let timer = null
    function inside() {
        console.log(new Date().toLocaleString()) // Print to see the time
        clearTimeout(timer) // Remove the previous timer, using the closure, inside access to variables outside its scope, that is, the timer under mySetInterval
        fn()
        timer = setTimeout(inside, delay)
    }
    timer = setTimeout(inside, delay)
    return { // Return an object clearTimeout() as the property value!
        clear() {
            clearTimeout(timer)
        }
    }
}

// Clear the timer
function myClearInterval(flagTimer) {
    flagTimer.clear()
}

/ / test
function testmySetInterval() {
    console.log('testmySetInterval')}const timer = mySetInterval(testmySetInterval, 1000)
// Call myClearInterval(timer)
Copy the code
encapsulation
function mySetInterval(fn, delay) {
    let timer = null
    function inside() {
        console.log(new Date().toLocaleString()) // Print to see the time
        clearTimeout(timer)
        fn()
        timer = setTimeout(inside, delay)
    }
    timer = setTimeout(inside, delay)
    return {
        clear() {
            clearTimeout(timer)
        }
    }
}

// Clear the timer
function myClearInterval(flagTimer) {
    flagTimer.clear()
}

/ / test
function testmySetInterval() {
    console.log('testmySetInterval')}const timer = mySetInterval(testmySetInterval, 1000)
// Call myClearInterval(timer)
Copy the code

RequestAnimationFrame replaces the timer

const RAF = {
      intervalTimer: null.timeoutTimer: null.setTimeout(cb, interval) { // Implement setTimeout
        let now = Date.now
        let stime = now()
        let etime = stime
        let loop = () = > {
          this.timeoutTimer = requestAnimationFrame(loop)
          etime = now()
          if (etime - stime >= interval) {
            cb()
            cancelAnimationFrame(this.timeoutTimer)
          }
        }
        this.timeoutTimer = requestAnimationFrame(loop)
        return this.timeoutTimer
      },
      clearTimeout() {
        cancelAnimationFrame(this.timeoutTimer)
      },
      setInterval(cb, interval) { // Implement setInterval
        let now = Date.now
        let stime = now()
        let etime = stime
        let loop = () = > {
          this.intervalTimer = requestAnimationFrame(loop)
          etime = now()
          if (etime - stime >= interval) {
            stime = now()
            etime = stime
            cb()
          }
        }
        this.intervalTimer = requestAnimationFrame(loop)
        return this.intervalTimer
      },
      clearInterval() {
        cancelAnimationFrame(this.intervalTimer)
      }
    }

    let count = 0
    function a() {
      console.log(count)
      count++
    }
    RAF.setTimeout(a, 1000)
Copy the code