setInterval 不是 运行 的确切时间间隔

setInterval is not run at exact interval

如果您创建一个非常简单的程序,其中有一个延迟为 1 秒的 setInterval,并记录其函数被调用的时间,您会注意到间隔 'drifts'.

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

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

漂移的原因是什么?

是否需要 5ms 重新排队 setInterval?

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

为什么 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 运行 秒后它将比预期 运行 半秒 'later'。

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

长话短说,none 的桌面操作系统是实时的 os

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

因此,不能保证在准确的时间内执行调用回调函数等任务。 os 最好兼顾所有任务,注意 power/resource 约束以优化整体性能。结果,时间有点浮动。

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

虽然标准浏览器上没有 Javascript 运行 声称是 real-time(正如在几条评论中指出的那样),但您可以采取一些步骤来使事情变得不那么顺利问题中的例子确实如此(错误是累积的)。

只是为了开始一个实验,我 运行 在我的 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 时会出现累积漂移,这一点在一分钟标记处可以看作是非常重要的:

所以问题是,是否可以采取任何措施使其跨浏览器变得更好一些?

此代码段放弃了 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 需要时间,设置一个新的间隔(在最后一个例子中)需要时间,但是 laptop/phone 等也会同时处理很多其他的东西,后台管理,监听中断等

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

了解不同系统上的其他人是否获得相同的结果会很有趣。不管怎样,基本信息是不要依赖于这样的超时,它不是一个实时系统。