使用阻塞代码的setTimeout行为

时间:2013-02-13 21:00:39

标签: javascript settimeout

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

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秒:打印“等待后”
  • Time 4 seoncds:打印“哟!”

4 个答案:

答案 0 :(得分:32)

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

以下是发生的情况:您安排setTimeout()在一秒钟后执行,然后阻止主线程3秒钟。这意味着忙碌循环结束的那一刻,超时已经太迟了2秒 - 并且JS引擎试图通过尽快调用你的超时来保持 - 也就是说,立即。

实际上这是:

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

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

答案 1 :(得分:8)

setTimeout的延迟与调用它的确切时间点有关。当你还在忙着等待时,它会过期。因此它将在下一个控制重新进入事件循环的瞬间执行。

编辑:

规范在这一点上有点模糊,但我想这是有意和唯一直接的解释:

  

setTimeout(function,milliseconds)

     

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

答案 2 :(得分:2)

当你在setTimeout调用之后运行忙等待循环时,你没有时间为你的“哟!”打印出来,因为Javascript运行时忙着你的循环(实际上空语句也因为循环条件的继续循环而使它忙碌)。

你应该总是避免这样一个繁忙的等待循环,因为在完成之前,没有其他任何东西可以在那个窗口中调用或运行。

答案 3 :(得分:0)

不确定是否可以帮助您,但是此问题的解释如下: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Run-to-completion

因此,第二个参数表示最短时间,而不是保证时间。