JS实现精确倒计时
实现倒计时对前端工程师来说,是很常见的需求。那么,要怎么实现精确的倒计时呢?
首先,考虑到客户端时间和服务端时间有误差,所以计算倒计时的时候,应该读取服务端的时间。但是,只考虑到这一点还远远不够的。页面运行时间长了,新打开页面的倒计时和原打开页面的倒计时还是存在误差。要减少这里的误差,就要说到Javascript解释器的工作原理。
Javascript解释器工作原理
Javascript解释器是单线程工作的,它执行任务按照任务进入队列的先后顺序执行。这会造成什么影响呢?
打个比方,设置定时器的时候,按照理想状况,下面的程序应当稳定的输出0。
let start = new Date().getTime()
let count = 0
setInterval(function(){
count++
console.log(new Date().getTime() - (start + count * 1000))
},1000)
由于代码执行占用时间,以及其他事件的阻塞,导致有些事件的执行延迟了几ms,阻塞事件不多时,影响微乎其微。但当我们添加更多的阻塞事件时,这个影响就会被放大,如下面的代码
let start = new Date().getTime()
let count = 0
setInterval(function(){
let j = 0
while(j++ < 100000000){}
},1)
setInterval(function(){
count++
console.log(new Date().getTime() - (start + count * 1000))
},1000)
线程阻塞解决方案
那么我们要怎么解决线程阻塞的问题呢?
按照正常的思路,如果没有被阻塞,下面设置好的定时器应该每隔1s执行一次。
setInterval(function(){},1000)
但是,如果出现阻塞事件,定时器可能就要隔1000+ms才执行一次。要精确的实现每隔1s执行一次,必须要先获取阻塞的时间。这里要用到定时器函数setTimeout控制定时器的执行时间,代码实现如下
setInterval(function(){
let j = 0
while(j++ < 100000000){}
},1)
let interval = 1000,
ms = 50000, //从服务器和活动开始时间计算出的时间差,这里测试用50000ms
count = 0,
startTime = new Date().getTime();
if( ms >= 0){
var timeCounter = setTimeout(countDownStart,interval);
}
第一部分的setInterval是一段阻塞代码。然后,我们分别定义了interval作为定时器的执行时间,距活动结束的时间用ms表示(ms=活动结束时间-服务器时间),
count表示计数器,然后启动定时器timeCounter。其中,countDownStart函数的实现逻辑如下
function countDownStart(){
count++;
let offset = new Date().getTime() - (startTime + count * interval);
let nextTime = interval - offset;
if (nextTime < 0) { nextTime = 0 };
ms -= interval;
console.log("误差:" + offset + "ms" );
if(ms < 0){
clearTimeout(timeCounter);
}else{
timeCounter = setTimeout(countDownStart,nextTime);
}
}
countDownStart的实现原理是,首先定义一个变量offset用来记录阻塞导致延误的时间是多少。nextTime代表offset和interval的差距,根据nextTime修改定时器timeCounter的执行时间,使它nextTime(ms)执行一次。
打个比方,如果上一次执行过程中因为阻塞延误了100ms,那么下一次就提前100ms启动定时器,即
timeCounter = setTimeout(countDownStart,900)
原文链接:https://blog.csdn.net/weixin_41672178/article/details/88372553