setTimeout 倒计时误差的原因和系统时间补偿

466 阅读2分钟

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 倒计时误差,可以考虑以下方法:

    1. 使用精确计时库:可以使用像 setInterval 或 requestAnimationFrame 这样的精确计时机制来实现准确的定时任务。

    2. 手动调整:在每次定时器触发后,通过记录实际执行时间并与预期执行时间进行比较,计算误差并进行手动调整,以纠正误差。

    3. 使用 Web Workers:将计时任务移至 Web Workers 中执行,这样可以避免与主线程的其他代码竞争,提高定时器的准确性。

    4. 使用时间戳:而不是依赖定时器的延迟时间,使用时间戳来进行倒计时和计算,可以更精确地控制时间。

// 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('倒计时结束')
})