zoukankan      html  css  js  c++  java
  • 使用 requestAnimationFrame 实现定时器,解决 setInterval 执行次数丢失问题

    来看这样一个场景:使用 setInterval 定时器倒计时,突然来了一个长达三秒的任务,定时器会有一次不准,两次丢失回调,导致少两次计算时间。

    // 在控制台上输入下面四行
    var second = 0
    setInterval(function() {
      console.log(`setInterval ${++second}`, new Date().getTime())
    }, 1000)
    
    // 几秒之后输入下面代码
    function sleep(ms) {
      const end = new Date().getTime() + ms
      console.log('sleep start')
      while (new Date().getTime() < end) {}
      console.log('sleep end')
    }
    sleep(3000)
    

    如图所示,少两次回调的执行。


    requestAnimationFrame 实现定时器

    requestAnimationFrame 传入一个回调函数,该回调函数会在浏览器下一次重绘之前执行,详情查看MDN文档 window.requestAnimationFrame

    /**
     * 设置精度定时器
     * @param {function} 回调函数
     * @param {number}   延迟时间
     * @return {number}  定时器ID
     */
    function setIntervalPrecision(callback, delay) {
      // 生成并记录定时器ID
      let obj = window.interValPrecisionObj || (window.interValPrecisionObj = { num: 0 })
      obj.num++
      obj['n' + obj.num] = true
      var intervalId = obj.num
      // 开始时间
      var startTime = +new Date()
      // 已执行次数
      var count = 0
      // 延迟时间
      delay = delay || 0
    
    
      ;(function loop() {
        // 定时器被清除,则终止
        if (!obj['n' + intervalId]) return
    
        // 满足条件执行回调
        if (+new Date() > startTime + delay * (count + 1)) {
          count++
          callback(count)
        }
    
        requestAnimationFrame(loop)
      })()
    
      return intervalId
    }
    
    /**
     * 清除精度定时器
     * @param {number} 定时器ID
     */
    function clearIntervalPrecision(intervalId) {
      if (window.interValPrecisionObj) {
        delete window.interValPrecisionObj['n' + intervalId]
      }
    }
    

    测试

    // 在控制台上输入下面四行
    setIntervalPrecision(function(val) {
      console.log(`setIntervalPrecision ${val}`, new Date().getTime())
    }, 1000)
    
    // 几秒之后输入下面代码
    function sleep(ms) {
      const end = new Date().getTime() + ms
      console.log('sleep start')
      while (new Date().getTime() < end) {}
      console.log('sleep end')
    }
    sleep(3000)
    

    任务阻塞结束后,会瞬间执行阻塞期间需要执行次数的回调,虽然倒计时页面会卡三秒(js特性),但实际剩余秒数不会出错。


    whosmeya.com

  • 相关阅读:
    在Linux系统中Navicat for MySQL 出现1045错误如何处理
    一个老程序员这些年的心得体会
    忘了
    DAY11
    day10_plus
    day10
    东北育才冲刺noip(day9)
    Java语言Socket接口用法详解
    JDBC-ODBC桥连接方式操纵SQL数据库
    JDBC连接SQL Server 2005步骤详解
  • 原文地址:https://www.cnblogs.com/whosmeya/p/14135507.html
Copyright © 2011-2022 走看看