【问题标题】:setInterval is not run at exact intervalsetInterval 不是以精确的时间间隔运行
【发布时间】:2021-09-04 04:37:08
【问题描述】:

如果您创建一个非常简单的程序,它有一个延迟 1 秒的 setInterval,并记录调用其函数的时间,您会注意到间隔“漂移”。

基本上,每次调用之间实际上需要(1,000 毫秒 + 一些时间)。

对于这个程序,每次调用之间实际上需要大约 1,005 毫秒。

漂移的原因是什么?

重新排队 setInterval 需要 5 毫秒吗?

是运行函数所需的时间长度吗? (我对此表示怀疑,但无法得出结论。)

为什么 setInterval 会以这种方式运行,而不仅仅是基于某个时钟时间? (例如,如果您有 1,000 毫秒的延迟并且您在时间 3 开始...只需检查 1,003 和 2,003 等是否已经过去?)

示例:

const startTime = new Date().valueOf();

function printElapsedTime(startTime) {
  console.log(new Date().valueOf() - startTime);
}

let intervalObj = setInterval(printElapsedTime, 1000, startTime);

输出: 1005 2010 3015 4020

所以你不再同步到 1 秒。由于它漂移了大约 5,因此在运行 100 次后,它将比预期“晚”运行半秒。

This question 讨论了如何避免这种漂移,但没有解释为什么会发生这种漂移。 (因为它并没有说 setInterval 在每次调用后递归地将自身添加到事件队列中 - 这需要 3ms ......这只是对漂移原因的猜测)。

【问题讨论】:

  • 链接的答案没有解释为什么存在漂移。如果您想避免漂移,它只是提供修复。
  • 规范只说了以下内容:“此 API 不保证计时器将按计划准确触发。由于 CPU 负载、其他任务等原因导致的延迟是可以预料的。” html.spec.whatwg.org/multipage/…
  • 或许熟悉 Chrome 或 Node 实现的人可以解答
  • 还有更多:步骤 17 允许算法在排队回调之前等待任意时间(如果我理解正确的话)“这是为了让用户代理将超时填充为需要优化设备的电源使用。例如,某些处理器具有低功耗模式,其中计时器的粒度会降低;在此类平台上,用户代理可以减慢计时器以适应此时间表,而不是要求处理器使用更准确的模式及其相关的更高功耗。” 如果内存正常,这会发生在没有聚焦的浏览器选项卡中。
  • 没有什么可回答的:超时只能保证至少您指定的时间量。如果它们需要更长的时间,那是符合规范的。然后,您还需要与所有现代浏览器都应用间隔限制的事实相结合,因此您会得到前几个“按要求”,之后最小间隔在前​​台变为 4ms,在后台变为惊人的“许多秒”。

标签: javascript setinterval


【解决方案1】:

虽然没有在标准浏览器上运行的 Javascript 声称是实时的(正如几个 cmets 中所指出的那样),但您可以采取一些步骤来使事情不会像问题中的示例那样失控(错误是累积的)。

刚开始我在 Windows 10 Chrome 上运行的实验:

const startTime = new Date().valueOf();

function printElapsedTime(startTime) {
  let curTime = new Date().valueOf();
  console.log(curTime - startTime);
}

let intervalObj = setInterval(printElapsedTime, 1000, startTime);
<div id="show">0</div>

这会产生相当一致的错误,每秒和大约一分钟你可以看到没有累积漂移:

但是,在同一系统上使用 Firefox 时会出现累积漂移,这可以从一分钟的时间来看非常显着:

所以问题是,有什么办法可以让它在浏览器之间变得更好一点吗?

这个 sn-p 抛弃了 setInterval,而是在每次调用时使用 setTimeout:

const startTime = new Date().valueOf();
let nextExpected = startTime + 1000;

function printElapsedTime(startTime) {
  let curTime = new Date().valueOf();
  console.log(curTime - startTime);
  let nextInterval = 1000 + nextExpected - curTime;
  setTimeout(printElapsedTime, nextInterval, startTime);
  nextExpected = curTime + nextInterval;
}

let intervalObj = setTimeout(printElapsedTime, 1000, startTime);
<div id="show">0</div>

在 Firefox 上,这给出了:

没有累积漂移,一分钟左右的误差也不比之前差。

所以,为了真正回答这个问题:

  1. 计算机确实有其他职责需要处理,并且不能保证在准确的时间处理超时函数(尽管规范要求它们在间隔过去之前不要处理)。在给定的代码中,console.log 需要时间,设置一个新的时间间隔(在最后一个例子中)需要时间,但笔记本电脑/手机等也将同时处理许多其他的东西,后台管理,监听中断等。

  2. 不同的浏览器似乎以不同的方式对待 setInterval - 规范似乎没有说明他们应该如何处理累积漂移。从这里的实验看来,Chrome/Edge 至少在我的 Windows10 笔记本电脑上做了一些缓解,这意味着漂移不是累积的,而 FF 似乎没有调整并且漂移可能很大。

想知道不同系统上的其他人是否得到相同的结果会很有趣。无论如何,基本消息是不要依赖这种超时,它不是一个实时系统。

【讨论】:

    【解决方案2】:

    长话短说,没有一个桌面操作系统是实时操作系统

    https://en.m.wikipedia.org/wiki/Real-time_operating_system

    因此,无法保证在准确的时间执行诸如调用回调函数之类的任务。操作系统最好兼顾所有任务,注意电源/资源限制以优化整体性能。结果,时间有点浮动。

    有趣的是,您会得到一致的 5 毫秒偏移。我对此没有任何解释

    【讨论】:

      猜你喜欢
      • 2018-06-20
      • 1970-01-01
      • 2010-11-19
      • 2015-05-26
      • 1970-01-01
      • 1970-01-01
      • 2019-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多