setInterval、setTimeout实现都会出现误差,这源于js的单线程。
他们的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行。
setInterval、setTimeout都属于宏任务
setInterval指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间。因此实际上,两次执行之间的间隔会小于指定的时间。比如,setInterval指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
为了确保两次执行之间有固定的间隔,可以不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间。
let leftCount = (new Date('2023-12-31').getTime()-new Date().getTime())/1000; //倒计时剩余秒数
let timer = '';
let leftDays = '';
let leftHours = '';
let leftSecond = '';
let leftMinute = '';
function startMove(){
timer = setInterval(()=>{
leftCount-=1;
leftSecond = Math.floor(leftCount%60);
leftMinute = Math.floor(leftCount/60%60);
leftHours = Math.floor(leftCount/60/60%24);
leftDays = Math.floor(leftCount/60/60/24);
console.log('剩余时间',leftDays+'天'+leftHours+'时'+leftMinute+'分'+leftSecond+'秒');
},1000)
if(leftCount<=0){
clearInterval(timer)
}
}
startMove(leftCount)
// 解决误差
const interval = 1000;
// 从服务器和活动开始时间计算出的时间差,这里测试用 50000 ms
let ms = 50000;
let count = 0;
const startTime = new Date().getTime();
let timeCounter;
if (ms >= 0) {
timeCounter = setTimeout(countDownStart, interval);
}
function countDownStart() {
count++;
const offset = new Date().getTime() - (startTime + count * interval);
let nextTime = interval - offset;
if (nextTime < 0) {
nextTime = 0;
}
ms -= interval;
console.log(
`误差:${offset} ms,下一次执行:${nextTime} ms 后,离活动开始还有:${ms} ms`
);
if (ms < 0) {
clearTimeout(timeCounter);
} else {
timeCounter = setTimeout(countDownStart, nextTime);
}
}
var start = new Date('2022-12-26').getTime();
var count = 0
let timer = setInterval(()=>{
count += 1;
console.log(new Date().getTime()-start);
if(count===10){
clearInterval(timer)
}
},1000)
// 误差demo
var start = new Date().getTime(); // 1672012800000
var num = 0
let intimer = setInterval(()=>{
num += 1;
// 如何求误差
// const offset = (new Date().getTime()-start)%1000
console.log('误差:',(new Date().getTime()-start));
console.log('误差:',new Date().getTime()-(start+1000*num));
if(num===10){
clearInterval(intimer)
}
},1000)