是立即执行子进程还是在事件循环的将来迭代中执行子进程?

时间:2018-08-09 03:38:03

标签: node.js

process_child.spawn()是否立即启动进程,还是等到当前执行上下文清除堆栈?

docs说“ spawn ...遵循其他Node.js API典型的惯用异步编程模式。”

这是否意味着将进程执行延迟为回调队列事件,在spawn()调用者的执行上下文清除堆栈后由事件循环处理?

我担心的是,在我的代码有机会为其安装处理程序之前,生成的进程可能会触发事件(例如,写入标准输出)。看来我并不是唯一一个对此感到担忧的人:https://github.com/nodejs/node-v0.x-archive/issues/4030#issuecomment-315392492

1 个答案:

答案 0 :(得分:1)

  

文档说“ spawn ...遵循其他Node.js API特有的惯用异步编程模式。”

     

这是否意味着将进程执行延迟为回调队列事件,在spawn()调用者的执行上下文清除堆栈后由事件循环处理?

不。这并不意味着。惯用的异步编程模式是在当前滴答声中启动异步操作,然后一旦启动并被其他方式执行或控制,则将控制权返回给Javascript。将来的事件将在此时传达其状态或结果。其他典型的异步操作(例如文件I / O或网络I / O)也是如此。

与大多数异步API一样,它会同步调用OS,以告知其启动其他应用,然后在将控制权交还给JS之前,确切地完成启动其他进程的实际工作量取决于操作系统和内部在特定OS上的实现。要更确切地了解返回之前等待的内容,您必须检查特定平台的本机代码源代码,然后再深入研究其在平台上使用的OS调用。

一般模型是启动异步操作,然后将控制权返回给JS。由于此异步操作中发生的事情是启动一个新进程,并且操作系统负责另一个进程中的实际工作,因此没有任何理由让node.js将实际调用操作系统的时间推迟到将来事件循环。无论如何,您都无法从新流程中获得任何事件,直到事件循环的未来滴答结束为止,因为这些事件都经过了事件队列。

遍历node.js源代码,它到达here in the source code,在libuv中调用uv_spawn()(跨平台库node.js构建于此)。在我看来,到目前为止一切都已经同步了(无需等待下一个刻度线)。下一步是研究libuv代码,以了解uv_spawn()的作用。

有趣的是,在遵循源代码时要注意的是,异常或同步错误是在内部捕获的,并在将来的价格变动时作为事件发出(不是同步的)。这样,您就可以从spawn()调用中返回函数返回的child_process对象,在其上安装事件处理程序,而不会错过任何事件。

uv_spawn()的源代码为here,它似乎在同一时刻对fork()进行了操作系统调用。

TLDR

因此,我看不到有任何目的将实际的OS调用推迟到以后的新进程的证据。似乎在同一时间点调用了操作系统。有证据表明,将错误报告推迟到以后进行,以简化界面的使用。

为您添加的其他内容,在开始新进程之前,先设置接收stdio和stdout的流。因为它们将通过JS事件队列从新进程接收数据,所以只要您在调用spawn()的同一滴答声中在那些流上设置自己的事件处理程序(例如,不在某些异步回调中),那么这些事件处理程序将在stdio和stdout流上接收并通知任何数据之前安装。这是node.js Javascript的单线程,事件驱动性质的产物。