likes
comments
collection
share

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

作者站长头像
站长
· 阅读数 16

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

引言

最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以解答这个问题。结果发现,setTimeout 并不完全可靠,因为它是一个宏任务。所指定的时间实际上是将任务放入主线程队列的时间,而不是任务实际执行的时间。

`setTimeout(callback, 进入主线程的时间)`

因此,何时执行回调取决于主线程上待处理的任务数量。

演示

这段代码使用一个计数器来记录每次 setTimeout 的调用。设定的间隔时间乘以计数次数,理想情况下应等于预期的延迟。通过以下示例,可以检查我们计时器的准确性。

function time () {
    var speed = 50, // 间隔
    count = 1 // 计数
    start = new Date().getTime();

    function instance() {
        var ideal=(count * speed),
        real = (new Date().getTime() - start);

        count++;
        console.log(count + '理想值------------------------:', ideal); // 记录理想值
        console.log(count + '真实值------------------------:', real); // 记录理想值

        var diff = (real - ideal);
        console.log(count + '差值------------------------:', diff); // 差值
        
        // 小于5执行
        if (count < 5) {
            window.setTimeout(function(){
                instance();
            }, speed);
        };
        
    };

    window.setTimeout(function () {
        instance();
    }, speed);
};

打印1

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

我们可以在 setTimeout 执行之前加入额外的代码逻辑,然后再观察这个差值。

...
window.setTimeout(function(){
    instance();
}, speed);
for(var a = 1, i = 0; i < 10000000; i++) { 
    a *= (i + 1); 
};
...

打印2

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

可以看出,这大大增加了误差。随着时间的推移,setTimeout 实际执行的时间与理想时间之间的差距会不断扩大,这并不是我们所期望的结果。在实际应用中,例如倒计时和动画,这种时间偏差会导致不理想的效果。

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

如何实现更精准的 setTimeout

requestAnimationFrame

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行,回调函数执行次数通常是每秒60次,也就是每16.7ms 执行一次,但是并不一定保证为 16.7 ms。

我们用requestAnimationFrame模拟 setTimeout

function setTimeout2(cb, delay) {
    const startTime = Date.now();

    function loop() {
        const now = Date.now();
        if (now - startTime >= delay) {
            cb();
        } else {
            requestAnimationFrame(loop);
        }
    }

    requestAnimationFrame(loop);
};

打印3

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

貌似误差问题还是没有得到解决,因此这个方案还是不行。

while

想得到准确的,我们第一反应就是如果我们能够主动去触发,获取到最开始的时间,以及不断去轮询当前时间,如果差值是预期的时间,那么这个定时器肯定是准确的,那么用while可以实现这个功能。

function time2(time) {
    const startTime = Date.now();
    
    function checkTime() {
        const now = Date.now();
        if (now - startTime >= time) {
            console.log('误差', now - startTime - time);
        } else {
            setTimeout(checkTime, 1); // 每毫秒检查一次
        }
    }

    checkTime();
}

time2(5000);

误差存在是 2, 甚至为 0, 但使用 while(true) 会导致 CPU 占用率极高,因为它会持续循环而不进行任何等待,会使得页面进入卡死状态,这样的结果显然是不合适的。

setTimeout 系统时间补偿

这个方案是在 Stack Overflow 看到的一个方案,我们来看看此方案和原方案的区别。

当每一次定时器执行时后,都去获取系统的时间来进行修正,虽然每次运行可能会有误差,但是通过系统时间对每次运行的修复,能够让后面每一次时间都得到一个补偿。

function time () {
    var speed = 50, // 间隔
    count = 1 // 计数
    start = new Date().getTime();

    function instance() {
        var ideal=(count * speed),
        real = (new Date().getTime() - start);

        count++;
        console.log(count + '理想值------------------------:', ideal); // 记录理想值
        console.log(count + '真实值------------------------:', real); // 记录理想值

        var diff = (real - ideal);
        console.log(count + '差值------------------------:', diff); // 差值
        
        // 5次后不再执行
        if (count < 5) {
            window.setTimeout(function(){
                instance();
            }, (speed - diff));
        };
    };

    window.setTimeout(function () {
        instance();
    }, speed);
};

打印4

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

setTimeout是准时的吗?引言 最近在一些论坛上,有人讨论 setTimeout 的准确性。因此,我进行了探索,以

结论

多次尝试后,是非常稳定的,误差微乎其微,几乎可以忽略不计,因此通过系统的时间补偿,能使 setTimeout 变得更加准时。

转载自:https://juejin.cn/post/7420059840971980834
评论
请登录