【每日一题】倒计时实现如何解决时间偏差?

共 2806字,需浏览 6分钟

 ·

2021-09-17 19:44

人生苦短,总需要一点仪式感。比如学前端~




setTimeout/setInterval 实现倒计时如何解决时间偏差?

造成时间误差的原因

在前端实现中一般会通过 setTimeoutsetInterval 方法实现一个倒计时效果。但是使用这些方法会存在时间偏差的问题,这是由于 js 的程序执行机制造成的。setTimeout 和 setInterval 的作用是隔一段时间将回调事件加入到事件队列中,由于JS的事件循环机制限制,加入到事件队列中的回调函数并不是立即执行的,它会等到当前执行栈为空的时候再被取出并执行。因此,事件函数等待执行的时间就是造成定时器时间误差的原因。

解决倒计时中的误差有两种办法:

  • 第一种就是通过前端定时向服务器发送请求获取最新的时间差,以此来校准倒计时时间。但是这样会存在一个很大的问题,就是每隔一秒去请求服务器,这样如果用户多了,服务器就会奔溃---内存占用率很大
  • 第二种方式是前端根据偏差时间来自动调整间隔时间的方式来实现的。这一方式首先是以 setTimeout 递归的方式来实现倒计时,然后通过一个变量来记录已经倒计时的秒数。每一次函数调用的时候,每次都将变量+1,然后根据这个变量和每次的间隔时间,我们就可以计算出此时无偏差时应该显示的时间。然后将当前的真实时间与这个时间相减,这样我们就可以得到时间的偏差大小,因此我们在设置下一个定时器的间隔大小的时候,我们就从间隔时间中减去这个偏差大小,以此来纠正由于程序执行所造成的时间误差。

const interval = 1000;
// 从服务器和活动开始时间计算出的时间差,这里测试用 50000 ms
let ms = 50000;
let count = 0//记录次数
const startTime = new Date().getTime(); //开始时间
let timeCounter;
if (ms >= 0) {
  timerCounter = setTimeout(countDownStart, interval); //返回一个对象
}
function countDownStart({
  count++;
  const offset = new Date().getTime() - (startTime + count * interval); //剩余的时间
  let nextInterval = interval - offset;
  if (nextInterval < 0) {
    nextInterval = 0;
  }
  ms -= interval;
  console.log(
    `误差:${offset} ms,下一次执行:${nextInterval} ms 后,离活动开始还有:${ms} ms`
  );
  if (ms < 0) {
    clearTimeout(timeCounter); //销毁定时器
  } else {
    timeCounter = setTimeout(countDownStart, nextInterval); //重开一个定时器
  }
}

执行结果图

可以看到,当延迟了2ms调用函数后,下次间隔会变成1000 - 2 = 998ms

代码图解




所有《每日一题》的 知识大纲索引脑图 整理在此:
https://www.yuque.com/dfe_evernote/interview/everyday


END
愿你历尽千帆,归来仍是少年。


让我们一起携手同走前端路!

关注公众号回复【加群】即可

浏览 108
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报