JavaScript中的超时和间隔是否使用单调时间?

Do timeout and interval in JavaScript use monotonic time?

假设我调用setTimeout(() => {...}, 1000),即等待1秒,但计算机的内部时钟有误。 500 毫秒后,OS 收到 NTP 更新,意识到它的时钟是未来 30 秒,因此将它的时钟调回 30 秒。我的超时时间什么时候触发? 500ms内,单调时间;或 30500 毫秒,挂钟时间?

如果它在 30 秒内...有没有办法设置一个计时器,该计时器将在 500 毫秒后正确触发 performance.now()

行为是否因浏览器而异?


注意:当然你永远不会获得高度准确的 setTimeout,因为浏览器缓解 Spectre 和旧代码依赖于最小延迟,但我的问题是由于内部时钟的变化导致时间的巨大跳跃.

通过 运行在不同的浏览器中使用以下代码片段并查看在不同的 OS 中更改 OS 时钟时间时的行为。

不同的测试: 测试 1:

  • 运行 片段
  • 设置未来的时间(30秒内)
  • 检查结果

测试 2:

  • 运行 片段
  • 设置过去的时间(30秒以内)
  • 检查结果

测试 3:

  • 运行 片段
  • 设置过去的时间(30秒以内)
  • 设置未来的时间(30秒内)
  • 检查结果

测试 4:

  • 运行 片段
  • 设置未来的时间(30秒内)
  • 设置过去的时间(30秒以内)
  • 检查结果

我的每个测试 运行 都重复了 5 次。

const perfor = () => {
  console.log(performance.now());

  window.requestAnimationFrame(perfor);
}

window.requestAnimationFrame(perfor);

setTimeout(() => {
  alert('HI');
}, 30000);

在 Windows 10(OS 版本 19041.508)上测试,所有浏览器都是 64 位版本: 测试 1:

                              | setTimeout           | perfor     |
------------------------------|----------------------|------------|
Firefox 81.0.2                | fired after ~30 secs | kept going |
Firefox 82.0.1                | fired after ~30 secs | kept going |
Google Chrome 86.0.4240.111   | fired after ~30 secs | kept going |
Microsoft Edge 44.19041.423.0 | fired after ~30 secs | kept going |

测试 2:

                              | setTimeout           | perfor                      |
------------------------------|----------------------|-----------------------------|
Firefox 81.0.2                | fired after ~30 secs | stopped when time rewinded* |
Firefox 82.0.1                | fired after ~30 secs | kept going                  |
Google Chrome 86.0.4240.111   | fired after ~30 secs | kept going                  |
Microsoft Edge 44.19041.423.0 | fired after ~30 secs | kept going                  |

测试 3:

                              | setTimeout           | perfor                      |
------------------------------|----------------------|-----------------------------|
Firefox 81.0.2                | fired after ~30 secs | stopped when time rewinded* |
Firefox 82.0.1                | fired after ~30 secs | kept going                  |
Google Chrome 86.0.4240.111   | fired after ~30 secs | kept going                  |
Microsoft Edge 44.19041.423.0 | fired after ~30 secs | kept going                  |

测试 4:

                              | setTimeout           | perfor                      |
------------------------------|----------------------|-----------------------------|
Firefox 81.0.2                | fired after ~30 secs | stopped when time rewinded* |
Firefox 82.0.1                | fired after ~30 secs | kept going                  |
Google Chrome 86.0.4240.111   | fired after ~30 secs | kept going                  |
Microsoft Edge 44.19041.423.0 | fired after ~30 secs | kept going                  |

* 在 Firefox 81.0.2 中,当我将相对论中的 OS 时间设置为过去时,performance.now() 的记录停止。因此,如果我设置未来的年、月或日,然后为我设置未来的年、月或日,但对于现在的机器,Firefox 81.0.2 将停止记录 performance.now() .我找不到原因来解释为什么会发生这种情况以及如何发生这种情况以及为什么它在 82.0.1 中得到修复。

注意:我可以在多个版本中测试 Firefox,因为我有一个更新。

我邀请其他用户运行这些并编辑以添加结果

performance.now() 将显示选项卡打开后的毫秒数。这在 Windows 10 中不会通过更改 OS 时钟时间来实现。