setTimeout() 触发比预期早一点

setTimeout() triggers a little bit earlier than expected

UPD:问题 What is the reason JavaScript setTimeout is so inaccurate? 询问为什么 JavaScript 中的计时器通常不准确,所有提到的不准确都是关于在规定的延迟。在这里我问为什么 NodeJS 甚至在延迟之前也容忍调用?定时器的设计不是很容易出错吗?

刚刚发现 NodeJS setTimeout() 的一个意想不到的(仅对我而言?)行为。有时它会比指定的延迟更早触发。

function main() {
  let count = 100;
  while (count--) {
    const start = process.hrtime();
    const delay = Math.floor(Math.random() * 1000);

    setTimeout(() => {
      const end = process.hrtime(start);
      const elapsed = (end[0] * 1000 + end[1]/1e6);
      const dt = elapsed - delay;
      if (dt < 0) {
        console.log('triggered before delay', delay, dt);
      }
    }, delay);
  }
}

main();

在我的笔记本电脑上输出是:

$ node --version
$ v8.7.0

$ node test.js
triggered before delay 73 -0.156439000000006
triggered before delay 364 -0.028260999999986325
triggered before delay 408 -0.1185689999999795
triggered before delay 598 -0.19596799999999348
triggered before delay 750 -0.351709000000028

是事件循环的"feature"吗?我一直认为至少要在delay毫秒后触发。

来自NodeJS docs

The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.

随着间隔数的增加(您有 100 个),准确度会降低,例如,1000 个间隔时准确度会更差。有十个就好多了。由于 NodeJS 必须跟踪更多间隔,因此其准确性会降低。

我们可以假设该算法有一个 "reasonable delta" 决定最终的准确性,它不包括检查以确保它在指定的时间间隔之后。也就是说,通过深入挖掘源代码很容易找到答案。

另请参阅 How is setTimeout implemented in node.js,其中包含更多详细信息,初步来源调查似乎证实了这一点和上述内容。