为什么while循环会阻塞节点事件循环?

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

var open = false; setTimeout(function() { open = true }, 1000) while (!open) { console.log('wait'); } console.log('open sesame'); 

作者解释了为什么while循环阻止执行,他说:

节点将永远不会执行超时callback,因为事件循环在第7行开始的while循环中停留,从不给它一个机会来处理超时事件!

然而,作者并没有解释为什么会发生在事件循环的背景下,或者真正发生了什么。

有人能详细说明吗? 为什么节点卡住了? 而如何改变上面的代码,同时保留while控制结构,这样事件循环就不会被阻塞,并且代码将按照人们可以合理预期的方式工作; 等待将在setTimeout触发前仅logging1秒,然后在logging“打开芝麻”之后,进程退出。

通用的解释,如关于IO和事件循环和callback这个问题的答案,并不真的帮助我理顺这一点。 我希望直接引用上面的代码的答案将有所帮助。

这真的很简单。 在内部,node.js由这种types的循环组成:

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

如果在某个时刻,事件队列中没有任何事情,那么就进入hibernate状态,直到事件队列中有事件发生。


所以,如果一段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循环会旋转,直到定时器触发,然后定时器将done值更改为true ,然后while循环完成,最后的console.log()将执行。 那不会发生什么事情。 这实际上是一个无限循环,并且console.log()语句永远不会被执行。

问题是,一旦你在while()循环中进入旋转等待,没有其他的Javascript可以执行。 所以,想要改变donevariables值的计时器不能执行。 因此,while循环条件永远不会改变,因此它是一个无限循环。

以下是JS引擎内部发生的情况:

  1. donevariables初始化为false
  2. setTimeout()从现在开始计划1秒的计时器事件
  3. while循环开始旋转
  4. 1秒进入while循环,定时器在JS引擎内部触发,定时器callback被添加到事件队列中。 这可能发生在JS引擎内部的另一个线程上。
  5. while循环继续旋转,因为donevariables不会改变。 因为它继续旋转,所以JS引擎永远不会完成这个执行线程,也永远不会从事件队列中取出下一个项目。

节点是一个单一的串行任务。 没有并行性,并发性是IO绑定的。 可以这样想:当一个IO调用阻塞/同步你的进程,直到返回数据时,所有东西都在一个线程上运行, 然而,如果我们有一个单独的线程,而不是等待IO(读取磁盘,抓取一个url等等),那么你的任务继续到下一个任务,在这个任务完成之后,它会检查这个IO。 这基本上是节点所做的事情,它是一个“事件循环”轮询IO,用于在循环上完成(或进度)。 所以当一个任务没有完成(你的循环)事件循环不会进行。 简而言之。

这是一个很好的问题,但我find了一个解决scheme!

 var sleep = require('system-sleep') var done = false setTimeout(function() { done = true }, 1000) while (!done) { sleep(100) console.log('sleeping') } console.log('finally, the done value changed!') 

我认为这是有效的,因为system-sleep不是一个等待。