8

这是我的测试代码(在这里小提琴):

console.log('Before wait');
setTimeout(function () { console.log('Yo!'); }, 1000);
var start = Date.now();
while (Date.now() < start + 3000) {}
console.log('After wait');

这是 Chrome 中事件的时间线:

  • 时间 0 秒:打印“等待前”
  • 时间 3 秒:打印“等待后”,然后在“哟!”之后立即打印

这种行为是否符合规范?为什么不是

  • 时间 0 秒:打印“等待前”
  • 时间 3 秒:打印“等待后”
  • 时间 4 秒:打印“Yo!”

?

4

4 回答 4

50

JavaScript 是单线程的。如果某些代码块使用执行线程,则无法执行其他代码。这意味着您的setTimeout()调用必须等到主执行(具有忙等待while循环的那个)完成。

这是发生的情况:您计划setTimeout()在一秒钟后执行,然后将主线程阻塞 3 秒钟。这意味着当您的繁忙循环结束时,超时已经晚了 2 秒 - JS 引擎试图通过尽快调用您的超时来跟上 - 即立即。

事实上这是:

while (Date.now() < start + 3000) {}

是 JavaScript 中最糟糕的事情之一。您将 JavaScript 执行线程保持 3 秒,并且无法执行其他事件/回调。通常浏览器会在这段时间内“冻结”。

于 2013-02-13T21:03:08.983 回答
9

的延迟与setTimeout调用它的确切时间点有关。当您仍在忙于等待时,它就会过期。因此它将在控件返回事件循环的下一个瞬间执行。

编辑:

规范在这一点上有点含糊,但我想这是预期的且唯一直接的解释:

setTimeout(函数,毫秒)

此方法在经过指定的毫秒数后调用该函数一次,直到通过调用 clearTimeout 取消。这些方法返回一个 timerID,可以在随后的 clearTimeout 调用中使用它来取消间隔。

于 2013-02-13T21:04:47.690 回答
2

当您在 setTimeout 调用之后运行忙等待循环时,您不会为“哟!”留出时间。打印出来,因为 Javascript 运行时正忙于你的循环(实际上空语句也使它忙,因为循环条件的继续评估)。

您应该始终避免这样的忙等待循环,因为在完成之前,在该窗口中不能调用或运行任何其他内容。

于 2013-02-13T21:07:23.007 回答
0

不确定它是否可以帮助您,但此问题已在以下位置进行了解释: https ://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion

出于这个原因,第二个参数表示最小时间——不是保证时间。

于 2020-06-08T16:08:58.240 回答