Node.js 书中给出了以下示例:
var open = false;
setTimeout(function() {
open = true
}, 1000)
while (!open) {
console.log('wait');
}
console.log('open sesame');
作者在解释 while 循环阻止执行的原因时说:
节点永远不会执行超时回调,因为事件循环是
停留在从第 7 行开始的 while 循环上,从不给它机会
处理超时事件!
但是,作者没有解释为什么会在事件循环的上下文中发生这种情况,也没有解释幕后到底发生了什么。
有人可以详细说明一下吗?为什么节点会卡住?以及如何更改上述代码,同时保留while
控制结构,以便事件循环不会被阻塞,并且代码将按照人们合理预期的方式运行;等待
之前只会记录 1 秒setTimeout
触发,进程在记录“芝麻开门”后退出。
通用解释,例如答案对于这个问题 https://stackoverflow.com/questions/14795145/how-the-single-threaded-non-blocking-io-model-works-in-node-js关于 IO 和事件循环以及回调并不能真正帮助我合理化这一点。我希望直接引用上述代码的答案会有所帮助。
这真的很简单。在内部,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 循环会一直旋转,直到计时器触发,然后计时器会更改done
to true
然后 while 循环将完成console.log()
最后会执行。那不会发生。这实际上将是一个无限循环console.log()
语句永远不会被执行。
问题是一旦你进入旋转等待while()
循环,没有其他 JavaScript 可以执行。所以,想要改变值的定时器done
变量无法执行。因此,while 循环条件永远不会改变,因此它是一个无限循环。
以下是 JS 引擎内部发生的事情:
-
done
变量初始化为false
-
setTimeout()
从现在开始安排 1 秒的计时器事件
- while 循环开始旋转
- while 循环旋转 1 秒后,计时器已准备好触发,但在解释器返回事件循环之前它无法实际执行任何操作
- while 循环不断旋转,因为
done
变量永远不会改变。因为它继续旋转,所以 JS 引擎永远不会完成该执行线程,也永远不会从事件队列中提取下一个项目或运行挂起的计时器。
Node.js 是一个事件驱动的环境。为了在实际应用中解决这个问题,done
标志会在未来的某些事件中发生变化。所以,而不是旋转while
循环中,您将为将来的某些相关事件注册一个事件处理程序并在那里完成您的工作。在绝对最坏的情况下,您可以设置一个循环计时器和“轮询”来经常检查该标志,但几乎在每种情况下,您都可以为将导致以下情况的实际事件注册一个事件处理程序:done
标记改变并在这方面做你的工作。正确设计的代码知道其他代码想要知道什么时候发生了变化,甚至可以提供自己的事件侦听器和自己的通知事件,人们可以注册感兴趣的事件,甚至只是一个简单的回调。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)