setTimeout 倒计时误差的原因和系统时间补偿
HTML5标准: html.spec.whatwg.org/multipage/t…
最小延迟执行问题 :zhuanlan.zhihu.com/p/614819835
原因:
1. setTimeOut 和 setInterval 最小执行时间为4s
1. setInterval 最小执行时间10ms
倒计时误差:
在js中,时间循环用于管理和调度代码的执行机制,setTimout函数用于设置一个定时器,在指定的时间间隔后执行延迟回调函数,而由于事件循环机制,setTimeout并不能保证在准确的时间间隔后执行回调函数, 而是将回调函数插入到事件队列中, 等待当前代码执行完毕后在执行
倒计时误差还可能是受以下影响:
-
延迟执行:setTimeout 设置的延迟事件并不是精确的时间点,而是一个最小延迟时间,如果时间循环中有其他代码正常执行,setTimeout函数 可能会被延迟执行
-
系统负载:当系统比较重的时候,事件循环可能出现延迟,这可能导致setTimeout的回调函数执行的时间比预期的较晚
-
睡眠模式: 在某些设备上,当设备进入睡眠模式时,定时器可能会暂停,直到设备被唤醒。这会导致 setTimeout 的回调函数执行时间延迟。
-
为了减少 setTimeout 倒计时误差,可以考虑以下方法:
-
使用精确计时库:可以使用像 setInterval 或 requestAnimationFrame 这样的精确计时机制来实现准确的定时任务。
-
手动调整:在每次定时器触发后,通过记录实际执行时间并与预期执行时间进行比较,计算误差并进行手动调整,以纠正误差。
-
使用 Web Workers:将计时任务移至 Web Workers 中执行,这样可以避免与主线程的其他代码竞争,提高定时器的准确性。
-
使用时间戳:而不是依赖定时器的延迟时间,使用时间戳来进行倒计时和计算,可以更精确地控制时间。
-
// setTimeout 补偿系统时间
function mySetTiemout(fn, delay, ...args){
let currentTime = Date.now()
let timer = null
const task = ()=>{
currentTime +=delay
timer = setTimeout(()=>{
fn.apply(this, args)
},currentTime - Date.now())
}
task()
return clearTimeout(timer)
}
//系统时间补偿,实现一个更加精确的setTimouta
function countDown(duration,callback) {
const startTime = performance.now()
function tick(){
const currentTime = performance.now()
const elapsed = currentTime - startTime
const remaining = duration - elapsed
if(remaining<= 0) {
callback()
} else {
setTimeout(tick, Math.max(0,remaining))
}
}
tick()
}
const duration = 6000
countDown(duration,()=>{
console.log('倒计时结束')
})