为什么setTimeout不取消我的循环?


75

我想知道一个JavaScriptwhile语句(在Chrome的控制台中)可以在一毫秒内递增一次变量的次数,因此我迅速将此代码段直接写入控制台:

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

问题是它永远运行。
为什么会发生这种情况,我该如何解决?


27
因为while循环不会使处理器产生超时时间
mplungjan 2014年

:也许这阅读将是有益的stackoverflow.com/questions/21463377/...
jillro

4
为什么这个问题如此强烈?

6
@remyabel我认为这是因为这实际上是演示单线程如何工作的一个很好的案例。在JS中,通常会假设间隔/超时会立即中断控制流并执行,而在某些情况下(如上所述),这种情况将永远不会发生。
MickMalone1983年

2
作为对以下答案的扩展,这是使用延迟的递归函数进行的操作:var run = true, i = 0; function loop() { i++; if (run) setTimeout(loop, 0); }; setTimeout(function() { run = false }, 1); loop();
Izkata 2014年

Answers:


80

这回到了JavaScript 1的单线程性质。发生的事情几乎是这样的:

  1. 您的变量已分配。
  2. 您计划要设置的功能run = false。这是计划运行后,目前的功能是运行(或任何其他当前处于活动状态)。
  3. 您将无休止地循环,并停留在当前函数内。
  4. 在无休止的循环(永不)之后,setTimeout()将执行和run=false

如您所见,一种setTimeout()方法在这里行不通。您可以通过检查while条件中的时间来解决此问题,但这会干扰您的实际测量。

1至少出于更实际的目的,您可以将其视为单线程。实际上,存在一个所谓的“事件循环”。在该循环中,所有函数将排队等待执行。如果将新功能排队,则将其放在该队列内的相应位置。的当前功能完成时,引擎将在下一个函数从队列(相对于定时作为引入,例如,通过setTimeout()并执行它。
如在每个时间点的结果只有一个功能被执行,从而使执行漂亮很多单线程事件都有一些例外,下面的链接中对此进行了讨论。


以供参考:


3
正如@Sirko所暗示的,要实现所需的效果,请将当前时间戳保存在变量中,var start = (new Date()).getTime();然后在while语句中从当前时间中减去该时间,并检查结果数是否小于预期的延迟。while ((new Date()).getTime() - start < delay)
无用的代码

1
如果您只是想了解Chrome使它运行的速度,请设置while循环以大约10,000的增量运行。设置一个变量,例如循环前的开始时间,然后循环后的当前时间,然后将两者进行比较。
SyntaxLAMP 2014年

“有时”正是堆栈为空的时候。
jillro 2014年

这并不是真的因为javascript的单线程性质。即使这是非常愚蠢的,但由于JS是一种解释型语言,因此实现可能毕竟是“检查每条指令之间的消息”。这是由于事件循环实现,在堆栈为空时调用该事件。
jillro 2014年

@Guilro至于“有时”-这个概念被称为讽刺,我想我用突出显示的“从不”来澄清。至于“消息之间”-我知道的每个JS引擎基本上都是单线程的(工人除外)。有关更深入的分析,请参阅stackoverflow.com/a/2734311/1169798。顺便说一句,大多数现代引擎只有在其更高级的功能失败时才直接使用JS。
锡尔科2014年

24

JavaScript是单线程的,因此,当您处于循环中时,不会执行任何其他操作。


2
调用JavaScript单线程时要小心,因为它具有处理多线程的能力。但是,标准执行是单线程的。
zzzzBov 2014年

19

为了保持Chrome的真实速度而不必不断获取时间来计算速度,可以尝试以下JS代码:

var start = new Date().getTime()
var i = 0
while(i<1000000)
{
    i++
}
var end = new Date().getTime()
var delta = end-start
var speed = i/delta
console.log(speed + " loops per millisecond")

4

Javascript是单线程的,这意味着它一次只能顺序运行一条指令。

像许多其他语言和库一样,事件系统由事件循环处理。事件循环基本上是一个循环,该循环在每次迭代时检查队列中的消息并调度事件。

在javascript中(如在实现该模式的语言中一样),当堆栈为空时(即,当所有函数都返回时),换句话说,在程序代码的末尾,调用事件循环。

您的“真实”程序在幕后看起来像这样:

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

while(true) {
/*
 * check for new messages in the queue and dispatch
 * events if there are some
 */
  processEvents();
}

因此,从时钟发出的超时超时消息永远不会被处理。

有关事件循环的更多信息,请访问:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop


当然,它有点复杂,请在此处查看以下示例:是否保证JavaScript是单线程的?tl; dr:在某些浏览器引擎中,某些外部事件不依赖于事件循环,而是在事件发生时立即触发,从而抢占了当前任务。但是setTimeout并非如此,它们只是向队列中添加一条消息而且永远不会立即射击。)



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.