setInterval 会漂移吗?

IT技术 javascript setinterval
2021-02-06 07:14:24

这真的是一个非常简单的问题。如果我使用setInterval(something, 1000),我能否完全确定在 31 天后它会准确触发“某事”60*60*24*31几次?或者有没有所谓的漂移的风险?

3个回答

简短回答:不,你不能确定。是的,它可以漂移。

长答案:John Resig 关于JavaScript 时间准确性JavaScript 计时器的工作原理

从第二篇文章:

为了理解计时器内部如何工作,需要探索一个重要概念:不保证计时器延迟。由于浏览器中的所有 JavaScript 都在单线程上执行,因此异步事件(例如鼠标单击和计时器)仅在执行中打开时才运行。

这两篇文章(以及该网站上的任何内容)都很棒,所以请阅读。

@chowey——虽然 Resig 的声明对于某些实现(例如 Chrome、Opera)可能是正确的,但在其他实现(例如 Safari、Firefox)中显然是错误的。
2021-03-16 07:14:24
它会尝试,但如果在 10 毫秒后执行被另一个长时间运行的函数阻止,它将漂移。另一个(愚蠢的)示例:如果您每 10 ms 执行一次的函数需要 11 ms 来运行怎么办?第二个函数什么时候运行?我相信 20 毫秒后(10 毫秒开始会跳过)
2021-03-17 07:14:24
基本上,一种更简单的方法是在计时器结束时可能发生用户事件(单击鼠标),这可能会中断该结束事件。当计时器应该关闭时,它可以占用该进程打开 CPU 的时间。
2021-03-18 07:14:24
我不明白。在第二篇文章中,John Resig 说“...setInterval 将尝试每 10 毫秒执行一次回调,而不管最后一次回调是何时执行的。” 这意味着 setInterval 没有漂移。
2021-04-07 07:14:24

这是您可以在 Firefox 中运行的基准测试:

var start = +new Date();
var count = 0;
setInterval(function () {
    console.log((new Date() - start) % 1000,
    ++count,
    Math.round((new Date() - start)/1000))
}, 1000);

第一个值应尽可能接近 0 或 1000(任何其他值显示触发时间的“离场”程度。)第二个值是代码被触发的次数,第三个值是多少次本来应该被触发的。您会注意到,如果您占用 CPU,它可能会偏离原点,但它似乎会自行纠正。尝试运行它更长的时间,看看它是如何处理的。

这是上面的实时版本,稍作调整以在浏览器中更清晰地显示输出:jsfiddle.net/hqmLg/1
2021-03-20 07:14:24
好吧,我在上面做的测试是在 Ubuntu FF 上,在大约 10 年前的 32 位笔记本电脑上进行的。我在一台新的 64 位 Windows 机器上尝试过,FF 更接近。IE 和 Chrome 的平均值在十分之二以内。
2021-03-29 07:14:24
driftlesssetInterval减轻漂移的替代品
2021-03-29 07:14:24
它永远不会为我纠正自己。如果我每秒执行 100 次之类的操作,它会达到约 85 次,但如果我放入控制台日志,它会加速约 30 次。- jsfiddle.net/pajtai/qXkNY(使用 Phrogz 演示)
2021-04-05 07:14:24

(抱歉我的英语不好)我在倒计时函数方面遇到了同样的问题,我编写了一个函数 countdown() 并使用 setInterval 进行循环,但每个循环漂移 1-3 毫秒。然后我写了一个函数来控制是否有任何漂移并修复它。

它只用真正的分钟和秒来控制。这里是。它对我来说很好用,我希望它也能帮助你。

$.syncInterval(functionname,interval,controlinterval)

例子:

countdown(){ some code };
$.syncInterval(countdown,1000,60);

它说每 1000 毫秒运行一次倒计时功能并每 60 秒检查一次

这是代码:

$.syncInterval = function (func,interval,control) { 
        var 
        now=new Date();
        realMinute=now.getMinutes(),
        realSecond=now.getSeconds(),
        nowSecond=realSecond,
        nowMinute=realMinute,
        minuteError=0,
        countingVar=1,
        totalDiff=0;

        var loopthat = setInterval(function(){

        if (nowSecond==0) {
            nowMinute++;
            nowMinute=nowMinute%60;
        };
        if (countingVar==0){

            now=new Date();
            realSecond=now.getSeconds();
            realMinute=now.getMinutes();

            totalDiff=((realMinute*60)+(realSecond))-((nowMinute*60)+(nowSecond));
            if(totalDiff>0){
                for (i=1;i<=totalDiff;i++) {
                    func();
                    nowSecond++;
                    countingVar++;
                };
            } else if (totalDiff==0){
                func();
                nowSecond++;
                countingVar++;
            } else if (totalDiff<0) {

            };
        } else {
            func();
            nowSecond++;
            countingVar++;
        };
        countingVar=countingVar%control;
        nowSecond=nowSecond%60;
    },interval);
};