【问题标题】:can setInterval drift over time?setInterval 可以随时间漂移吗?
【发布时间】:2018-11-16 09:06:43
【问题描述】:

我有 2 个 node.js 网络服务器。我在网络服务器中缓存数据。我根据系统时间同步缓存加载/清除。我已经完成了所有主机的时间同步。

现在我使用以下代码每 15 分钟清除一次缓存:

millisTillNexthour = "Calculate millis remaining until next hour"

setTimeout(function() {
  setInterval(function() {
    cache.clear();
  }, 60000*15);
}, millisTillNexthour);

我的期望是,即使这个过程永远运行,缓存也会在一天中的每个小时的第 15 分钟被清除一次。

我的问题是:setInterval 会随时间漂移吗?

例如:现在它清除10:00 10:15 10:30 10:45 11:00 ......的缓存

是否会发生 setInterval 不是 10:15 系统时间,而是在 10:20 系统时间执行,而它应该在 10:15 清除缓存??

我不确定这是如何工作的。请阐明一些观点。我希望我能很好地解释我的问题。

【问题讨论】:

  • 除非有其他东西阻止了它的执行,否则它应该尽可能靠近:15 执行,尽快
  • 理论上没有。 setInterval 专门设计用于解决使用 setTimeout 调用自身(并且会漂移)所带来的问题。然而,伊恩是对的;如果在它应该运行回调的时候有什么东西阻塞了它 5 分钟,它在阻塞解除之前不会运行。不要依赖它进行金融交易或核威慑。
  • 我发现了另一个类似的帖子:stackoverflow.com/questions/985670/will-setinterval-drift

标签: javascript node.js


【解决方案1】:

我在这里聚会可能有点晚了,但这就是我刚才解决这个特殊的时间滑动问题的方法,使用递归调用的 setTimeout() 函数而不是使用 setInterval()。

var interval = 5000;
var adjustedInterval = interval;
var expectedCycleTime = 0;

function runAtInterval(){
    // get timestamp at very start of function call
    var now = Date.now();

    // log with time to show interval
    console.log(new Date().toISOString().replace(/T/, ' ').replace(/Z/, '') + " runAtInterval()");

    // set next expectedCycleTime and adjustedInterval
    if (expectedCycleTime == 0){
        expectedCycleTime = now + interval;
    }
    else {
        adjustedInterval = interval - (now - expectedCycleTime);
        expectedCycleTime += interval;
    }

    // function calls itself after delay of adjustedInterval
    setTimeout(function () {
        runAtInterval();
    }, adjustedInterval);
}

在每次迭代中,函数都会根据之前计算的预期时间检查实际执行时间,然后从“interval”中减去差值以产生“adjustedInterval”。这种差异可能是正的也可能是负的,结果表明实际执行时间倾向于在“真实”值 +/- 5 毫秒附近波动。

无论哪种方式,如果您有一个每分钟执行一次的任务,并且您运行一整天,则使用此函数您可以预期 - 在一整天内 - 每个小时将有 60 次迭代发生。您不会遇到只得到 59 个结果的偶然时间,因为最终一分钟已经过去了。

【讨论】:

    【解决方案2】:

    setInterval 肯定是在漂移(尽管我同意它不应该如此)。我正在运行一个间隔为 30 秒的 Node.js 服务器。在每个滴答声中,都会发出一些异步 Web 请求,从开始到结束大约需要 1 秒。在中间的 29 秒内没有发生其他用户级/应用程序处理。

    但是,我从我的服务器日志中注意到,在 30 分钟的过程中,发生了 100 毫秒的漂移。当然,漂移不能归咎于底层操作系统,只能是 Node.js 设计或实现的一些缺陷。

    【讨论】:

      【解决方案3】:

      我很失望地注意到在 NodeJS 的 setInterval 实现中有一个错误。请看这里:

      https://github.com/nodejs/node/issues/7346#issuecomment-300432730

      【讨论】:

        【解决方案4】:

        您可以使用 Date() 对象来设置特定的时间,然后将特定的毫秒数添加到日期。

        【讨论】:

        • 这不会比他们已经拥有的提供额外的健壮性。
        • 除非它基于包含 24 小时中每十五分钟时间的表示的对象或数组,并且缓存在事件中被清除。不过,只是在这里大声思考。
        • 在内部,setInterval 使用系统时钟。需要明确的是,setInterval 永远不会漂移,但它可能必须在触发前等待(如果在它应该运行的时候有什么东西阻塞了它)。这适用于您可能做的所有事情,除了接管内核职责和热等待,尽管在其他线程更普遍的语言中它不是一个问题(可能导致它阻塞的事情更少)。
        • 好的。你是对的。在这种情况下——如果该函数在 Web Worker 上运行会怎样?这会有帮助吗?
        【解决方案5】:

        由于 Javascript 的工作原理,它绝对可以 (See Event Loop)

        当其他排队事件完成时,Javascript 事件循环执行 setInterval 队列。这些事件将需要一些时间,并且会影响您的 setInterval 函数的执行时间,并且最终会随着时间的流逝而消失。

        【讨论】:

          【解决方案6】:

          setInterval 不应该在完美世界中漂移。由于其他事情占用系统资源,它可能会延迟。如果您需要更精确的解决方案,请使用 clock() 函数来“校准”您的节点。

          【讨论】:

            猜你喜欢
            • 2010-11-02
            • 2013-12-15
            • 2018-09-10
            • 2016-10-07
            • 1970-01-01
            • 2018-02-17
            • 1970-01-01
            • 2013-05-12
            • 1970-01-01
            相关资源
            最近更新 更多