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

我想知道一个 JavaScript while语句(在 Chrome 的控制台中)能在一毫秒内增加多少次变量,所以我迅速将这段代码直接写到了控制台中:

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

问题是它会永远运行下去。
为什么会这样,我该怎么解决?

3827 次浏览

这又回到了 JavaScript1的单线程特性:

  1. 你的变量已经分配好了。
  2. 你调度一个函数,设置 run = false。计划运行 之后,当前函数正在运行(或者当前活动的其他任何东西)。
  3. 你有你的无限循环,并且停留在当前函数之内。
  4. 在无限循环(永远不会)之后,将执行 setTimeout()回调和 run=false

正如您所看到的,setTimeout()方法在这里不起作用。您可以通过检查 while条件中的时间来解决这个问题,但是这会干扰您的实际测量。

1 至少在更实际的情况下,你可以把它看作是单线程的。实际上有一个所谓的“事件循环”。在这个循环中,所有函数都排队等待,直到它们被执行。如果您将一个新函数排队,它将被放置在该队列中的相应位置。当前函数完成后,引擎从队列中获取下一个函数(关于引入的时间,例如 setTimeout()引入的时间)并执行它。
因此,在每个时间点只执行一个函数,从而使执行几乎是单线程的。对于事件也有一些例外,在下面的链接中讨论。


参考资料:

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

为了保持 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")

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 是否保证是单线程的?(医生:在一些浏览器引擎中,一些外部事件不依赖于事件循环,当它们发生时会立即激发,抢先当前任务。但是 setTimeout 不是这种情况,它只是向队列添加一条消息,并且永远不会立即触发。)

While 循环不访问 setTimeout。您的代码设置运行为 true,那么它就永远不会变为 false。

JavaScript 有单线程,并且在任何地方都有单线程。

我认为这个问题很好: JavaScript 是否保证是单线程的?

当您的代码在循环中时,其他代码不执行并阻塞。