使用setInterval执行定时器造成的倒计时误差问题修改

前言

最近在做微信小程序直播间秒杀抢券的功能,功能完成提测后,测试过来说这倒计时不对呀,在不同手机上倒计时有误差。听到反馈,第一反应就是又要掉头发了。于是,当即开始查找资料,定位问题,解决后,写此文章做个记录。

注意:用来计算倒计时的当前时间,不可以取用户客户端的时间(因为客户端时间用户可以随时调整,会造成时间不一致),应该是取后端返回的服务器的时间。

1、最初使用setInterval执行倒计时定时器,由于javascript是单线程的,同一时间只能执行一个js代码(同一时间其他异步事件执行会被阻塞 ) ,导致定时器事件每次执行都会有时间误差,甚至误差会越来越大。
最初代码如下:

Page({
  /**
   * 页面的初始数据
   */
  data: {
    timer: null, // 定时器
    countdownEndTime: '', // 倒计时结束时间 即开始时间
    countdownTime: '抢', // 倒计时显示
  },

  // 倒计时
  countTime: function () {
    var that = this;
    let countDownNum = that.getCountdownTime(); //获取倒计时初始值
    if (countDownNum <= 0) return;
    var string = that.getCountdownString(countDownNum);
    that.setData({
      countdownTime: string
    });

    var interval = 1000
    that.data.timer = setInterval(function() {
      countDownNum -= interval;
      var string = that.getCountdownString(countDownNum);
      if (countDownNum <= 0) {
        clearInterval(that.data.timer);
      } else {
        that.setData({
          countdownTime: string
        })
      }
    }, interval)
  },

  getCountdownString: function (time) {
    var m = Math.floor(time / 1000 / 60 % 60);
    var s = Math.floor(time / 1000 % 60);
    if (m <= 0 && s<= 10) {
      return s;
    }
    s = s < 10 ? "0" + s : s;
    m = m < 10 ? "0" + m : m;
    var string = `${m}:${s}`;
    return string;
  },

  getCountdownTime: function () {
    var that = this;
    var time = that.data.countdownEndTime - that.data.dataTimestamp;
    return time;
  }
})

2、首先我们来看一个定时器执行时间测试:

var start = new Date().getTime(), count = 0
setInterval(function () {
  count++
  console.log(new Date().getTime() - (start + count * 1000) + 'ms')
}, 1000)

目测代码运行结果,定时器每秒执行一次,每次输出应该是0,但结果并非如此,实际输出如下:
在这里插入图片描述
结论:由于代码执行占用时间和其他事件阻塞原因,导致定时器事件执行延迟了几ms,但影响较小。

加下来加一段阻塞线程的代码,再次测试:

// 占用线程事件
setInterval(function () {
  var n = 0
  while (n++ < 1000000000);
}, 1000)

var start = new Date().getTime(), count = 0
setInterval(function () {
  count++
  console.log(new Date().getTime() - (start + count * 1000) + 'ms')
}, 1000)

执行结果输出如下:
在这里插入图片描述

结论:由于加了很占线程的阻塞事件,导致定时器事件每次执行延迟越来越严重。

由于实际项目中,执行计时器的同时,会有很多其他异步阻塞事件,会导致倒计时功能不精确。

3、解决方案

通过引入计数器,判断计时器延迟执行的时间来进行误差修正,尽量让误差缩小,不同浏览器不同时间段打开页面倒计时误差可控制在1s以内。

最终代码修改为setTimeout:

// 倒计时
 countTime: function () {
   var that = this;
   let countDownNum = that.getCountdownTime(); //获取倒计时初始值
   if (countDownNum <= 0) return;
   var string = that.getCountdownString(countDownNum);
   that.setData({
     countdownTime: string
   });
   var interval = 1000, start = new Date().getTime(), count = 0;
   that.data.timer = setTimeout(countDownStart, interval);
   function countDownStart() {
     var offset, nextTime; // offset是倒计时误差时间,nextTime是减去误差时间后下一次执行的时间
     count++;
     offset = new Date().getTime() - (start + count * interval);
     nextTime = interval - offset;
     if (nextTime < 0) { nextTime = 0; }
     countDownNum -= interval;
     var string = that.getCountdownString(countDownNum);
     console.log("误差: " + offset + "ms, 下一次执行: " + nextTime + "ms后,离活动开始还有: " + countDownNum + "ms");
     if (countDownNum <= 0) {
       clearTimeout(that.data.timer);
     } else {
       that.setData({
         countdownTime: string
       })
       that.data.timer = setTimeout(countDownStart, nextTime);
     }
   }
 }

运行结果如下:
在这里插入图片描述

参考文章:https://www.jianshu.com/p/24895fdba736

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值