朴灵的《深入浅出node.js》中关于事件循环的描述是不是错的?
发布于 11 天前 作者 lianghz 1114 次浏览 来自 问答

该书对我学习nodejs帮助不少,感谢朴灵。 但读到关于事件循环章节就看不下去了,因为理解不到其对事件循环原理的解释。 他提到这么一段,大意是:在node进程启动时,node就创建一个while循环,检查观测者(事件队列)中是否有事件存在,如果存在就取出事件,执行回调函数后继续循环,如果不存事件就退出进程。 他举了一个生动的例子:餐馆的厨师获取前台妹子收到的客户订单,之后根据订单做菜。做完一单取下一单,如此往复。如果没有订单就下班打烊和妹子约去了。

我疑问就来了,餐馆的老板朴灵对员工这么好吗?是不是厨师早上来上班,如果还没有客人来吃饭,就可以马上下班了,之后来的客人就只能吃闭门羹了?

同样的问题,如果有一个很耗时的IO操作,IO还未完成,还未把事件给到观测者,则在while循环中,还没有在观测者中取得事件。按朴灵的说法,该循环就要退出进程了。那么当这个IO完成后,这个事件回调就不会执行了?

再问个问题,node进程启动,是不是指在CMD执行 node app.js之后?这时就产生循环了?

下面是我对循环的理解,请大神指点纠正,谢谢! 1、假设我们在一个web应用中请求一个链接,执行了一个js文件,js文件代码按顺序同步执行,遇到IO操作,node api把这些异步操作扔给线程池处理,并在队列中记录这个IO操作,继续向下执行,记录所有的IO操作到队列中; 2、执行了js文件的最后一行后,才生成一个事件循环,循环检查第一步中记录了IO操作的队列,检查队列中的记录是否有对应的事件,如果有就取出事件执行回调,如果没有就继续循环下去; 3、继续循环检查队列中的记录,直到所有记录都产生了事件,并执行回调。这时才结束循环退出进程; 4、如果上面的循环还未结束,我们又请求了一个链接,这个请求的js程序就等待上面的循环完成才开始执行(因为是单线程web服务,所有请求都是在同一个线程中,循环也是在相同的线程中运行,所以要等待)。这个js程序也像2、3步一样的过程,在程序执行到最后一行之后,又生成一个循环,检查队列记录,执行事件。

by lhz

17 回复

事件循环机制不止node有,你可以看安卓的loop epoll

来自酷炫的 CNodeMD

io 操作没完成怎么能说 loop 中就没有事件了?io 事件不还在么,当然不会退出了。

来自酷炫的 CNodeMD

想要看到更多细节建议直接翻源代码吧,你想知道的在 node.cccore.cc 两个文件里写的很清楚

来自酷炫的 CNodeMD

@zswnew,@hyj1991 嗯,书中说事件循环检查观察者有没有事件,事件是在IO完成后放入观察者中。 另外,他说事件循环是在进程启动后就开始,循环又在javascript线程中执行,我纳闷这样不阻塞吗,所有才怀疑是在程序最后才生成循环的? 我再看看代码琢磨一下,谢谢2位!

我只能说用现实例子来类比复杂的 eventloop 过程是有点强人所难,因为意思大概能到,但实际上的细节差别可能非常大。

这个例子中。并不是说没客人来吃饭,就可以下班了。关键还要看前台妹纸,前台妹纸没下班,厨师能下班?

以定时器为例,当(第一次)执行 setTimeout 的时候,会建立 timer watcher, timer watcher 持有这个 handler。 eventloop 执行的过程中,会询问 timer watcher, 是否有 event,如果有,处理 event,看情况决定是否取消持有 handler(setInterval 会多次产生 event)。

解除 watcher 对 handler 的持有关系有两种,一种是自然执行结束,另一种是通过 unref。

当所有 watcher 都不再持有 handler 了,eventloop 就会退出。

我正在写作第二版的《深浅》,将会更少从感性上去描述这个过程。

主文件执行后,Node 会主动调用一次 process.nextTick() 来进入 eventloop。

@JacksonTian 期待第二版,必买

你就这样想吧,非回调函数全部执行了(如果这时遇到回调函数就会塞到一个队列里面,fs的回调除外), 再用while循环执行回调函数的那个队列(期间可能回调函数会不断生成) 。 这个队列没代码要执行了,while就结束了,接下来就退出进程了。 虽然专业术语可能有偏差和实际运行上有些遗漏,但基本实现你可以这样理解。 而且我这个大白话文版应该很好理解了 哈;)

接下来,是问答环节: Q:你纳闷这样不阻塞吗? A:当然会阻塞,够快不就好了,这也是node不适合CPU密集运算的原因。 Q:读写文件怎么办,这不会阻塞吗? A:所以fs的回调他是使用线程池啊。 Q:会阻塞干嘛还用队列? A:这其实是保证了CPU不会在等待的时候浪费

@JacksonTian 解析后大概明白了。 @zy445566 没错,你的理解和我本身的理解是一样的。我纳闷阻塞是因为书中提到while是在node进程启动的时候生成,我就想你这么早就while,下面的代码有机会执行吗?JacksonTian 也回复了,是在主文件执行后才进入eventloop,执行event。待所有event执行完才退出。

哈哈,作者本人来了

@ianchn 这才是一个负责任的作者。力挺啊。

我好像觉得,你在用思考js的套路来思考node的搭建。。。但是,,单纯用js是写不出node来?(比如你考虑执行循环之后阻塞了,是否是在主文件加载完后再执行evenloop),个人觉得node的这种事件循环机制可以看作是另类的系统信号,,至于如何做到这个信号,和程序无关(nodejs代码)和系统有关(node本身),。。当然,我是彩笔,只是凑凑话题,具体以大神为准

我之前关于Node 网络异步IO的分析(参照了社区的文章进行分析): http://luoxia.me/code/2017/07/27/libuv%E7%BD%91%E7%BB%9CIO%E6%9C%BA%E5%88%B6/

实际上node执行的用户代码是在V8单独的线程执行,node主进程实际上在进行了一系列初始化后,进入了一个event loop,只有当没有任何handle和request的时候,循环才结束。

@lianghz 不用谢,我邀请的 😜

@JacksonTian 好奇啥时候出第二版,真的好想买一本

@JacksonTian 同样期待,必买啊!!!

哇,我刚看了这本书的时间循环,直接就过去了,为啥我就没有你这样的思考呢,怪不得自己这么菜(佩服lz看的挺仔细的,挺会思考的)? @JacksonTian 期待作者的第二版

回到顶部