解决倒计时时间偏差问题

2022-07-17 06:06:39
2024-10-05 17:48:35

产生原因

由于jsEventLoop执行机制,setTimeout/setInterval只是将事件插入了任务队列,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()设定的延迟回调时间执行。所以此时 等待执行的实际时间 = 等待执行栈为空的时间 + 设定的延迟回调时间,此时时间偏差就产生了,即 时间偏差 = 等待执行栈为空的时间

可以这么去理解,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得空闲时间执行,也就是说,尽可能早的执行。它在任务队列的尾部添加一个事件,因此要等到同步任务和任务队列现有的事件都处理完,才会得到执行,而不是设定的回调时间0就会立刻去执行了。

解决办法

方法一

以后端返回服务器时间为准,通过定时向服务器发送请求,获取最新的时间时间偏差,以此来校准倒计时时间。但是这种方法消耗服务端资源较大,会存在并发问题;而且接口请求返回时间本身也存在时间偏差,增加了问题的不可控因素与复杂度

方法二

思路:
用递归的方法执行倒计时,在每次递归调用setTimeout回调的时候,计算出时间偏差,在下一次执行setTimeout时,把原设定的延迟回调时间减去时间偏差即可。

js 复制代码
    // 设定倒计时规则为每秒倒计时
    const interval = 1000 
    // 设定总倒计时长为30s
    let totalCount = 30000 
    // 记录递归已执行次数,以倒计时时间间隔 interval=1s 为例,那么count就相当于如果没有时间偏差情况下的理想执行时间
    let count = 0 
    // 记录程序开始运行的时间
    const startTime = new Date().getTime(); 
    let timeoutID = setTimeout(countDownFn, interval)
    
    // 倒计时回调函数
    function countDownFn() {
    // count自增,记录理想执行时间
        count++ 
        // 获取当前时间-刚开始记录的startTime-理想执行时间得到时间偏差=等待执行栈为空的时间
        const offset = new Date().getTime() - startTime - count * interval
        // 根据时间偏差,计算下次倒计时设定的回调时间,从而达到纠正的目的
        let nextTime = interval - offset 
        if (nextTime < 0 ) {
            nextTime = 0
        }
        totalCount -= interval
        if (totalCount < 0) {
            clearTimeout(timeoutID)
        } else {
            timeoutID = setTimeout(countDownStart, nextTime)
        }
    }
目录

运营需要亿点资金维持,您的支持,是小白龙创作的动力!!!

昵称
留言
赞赏金额
暂无评论,欢迎留下你的评论