赞
踩
最近在做微信小程序直播间秒杀抢券的功能,功能完成提测后,测试过来说这倒计时不对呀,在不同手机上倒计时有误差。听到反馈,第一反应就是又要掉头发了。于是,当即开始查找资料,定位问题,解决后,写此文章做个记录。
注意:用来计算倒计时的当前时间,不可以取用户客户端的时间(因为客户端时间用户可以随时调整,会造成时间不一致),应该是取后端返回的服务器的时间。
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。