一尘不染

为什么while循环会阻止事件循环?

node.js

以下示例在Node.js书中给出:

var open = false;

setTimeout(function() {
  open = true
}, 1000)

while (!open) {
  console.log('wait');
}

console.log('open sesame');

解释了while循环为何阻止执行时,作者说:

节点将永远不会执行超时回调,因为事件循环卡在了此循环中,而循环在第7行上开始了,因此永远不会给它处理超时事件的机会!

但是,作者没有解释为什么这是在事件循环的背景下发生的,还是在幕后真正发生了什么。

有人可以详细说明吗?为什么节点卡住?以及如何在保持while控制结构不变的情况下更改上述代码,以使事件循环不会被阻塞,并且代码将像人们合理预期的那样运行;在setTimeout发生火灾之前,只会记录1秒钟的等待时间,然后在记录“打开芝麻”后退出该过程。

诸如此类的关于IO和事件循环以及回调的问题的答案之类的通用解释并不能真正帮助我合理化。我希望直接引用上述代码的答案会有所帮助。


阅读 343

收藏
2020-07-07

共1个答案

一尘不染

真的很简单。在内部,node.js包含以下类型的循环:

  • 从事件队列中获取一些东西
  • 运行指示的任何任务并运行直到返回
  • 完成上述任务后,从事件队列中获取下一项
  • 运行指示的任何任务并运行直到返回
  • 反复冲洗,起泡,重复

如果在某个时刻事件队列中什么都没有,则进入睡眠状态直到事件队列中放置了某些东西。


因此,如果一段Javascript处于while()循环中,则该任务没有完成,并且按照上述顺序,在完成之前的任务之前,不会从事件队列中挑出任何新东西。因此,一个很长甚至永远运行的while()循环只会加重工作量。因为Javascript一次仅运行一个任务(用于JS执行的单线程),所以如果一个任务在while循环中旋转,那么其他任何事情都将无法执行。

这是一个简单的示例,可能有助于解释它:

 var done = false;

 // set a timer for 1 second from now to set done to true
 setTimeout(function() {
      done = true;
 }, 1000);

 // spin wait for the done value to change
 while (!done) { /* do nothing */}

 console.log("finally, the done value changed!");

从逻辑上讲,有些人可能会认为while循环将旋转直到计时器触发,然后计时器将更改doneto
的值,true然后while循环将完成,最后的while
console.log()将执行。那不会发生。实际上,这将是一个无限循环,并且该console.log()语句将永远不会执行。

问题是,一旦您进入while()循环等待循环,就无法再执行其他Javascript。因此,想要更改变量值的计时器done无法执行。因此,while循环条件永远不会改变,因此它是一个无限循环。

这是JS引擎内部发生的事情:

  1. done 变量初始化为 false
  2. setTimeout() 从现在开始安排1秒的计时器事件
  3. while循环开始旋转
  4. 在while循环旋转1秒后,计时器会在内部触发JS引擎,并将计时器回调添加到事件队列中。这可能发生在JS引擎内部的其他线程上。
  5. while循环保持旋转,因为done变量永不改变。因为它继续旋转,所以JS引擎永远不会完成该执行线程,也永远不会从事件队列中提取下一项。

node.js is an event driven environment. To solve this problem in a real world
application, the done flag would get changed on some future event. So,
rather than a spinning while loop, you would register an event handler for
some relevant event in the future and do your work there. In the absolute
worst case, you could set a recurring timer and “poll” to check the flag ever
so often, but in nearly every single case, you can register an event handler
for the actual event that will cause the done flag to change and do your
work in that. Properly designed code that knows other code wants to know when
something has changed may even offer its own event listener and its own
notification events that one can register an interest in or even just a simple
callback.

2020-07-07