node 子进程 - 你需要了解的一切

Node.js 单线程事件循环适合 I/O 密集型任务;当需要执行外部命令、隔离运行环境或利用多核 CPU 时,就需要 child_process 与 cluster 模块来派生子进程。

来源整理自:vue3js.cn 面试官系列

衍生一个子进程

spawn 函数在一个新的进程里启动一个命令,可以给这个命令传递任何的参数。

另外一些我们可以为 ChildProcess 实例注册的事件有 disconnect、error、close、message。

  • 当父进程手动调用 child.disconnect() 的时候,disconnect 事件会被触发。
  • 如果进程不能被衍生(spawn)或者被 killed,error 事件被触发。
  • 当一个子进程的 stdio 关闭的时候,close 事件被触发。
  • message 事件是最重要的一个。当子进程使用 process.send() 函数发送信息的时候,message 事件会被触发。这就是父子进程相会交流的方式。我们将会在下面看到一个例子。

每一个子进程都会得到三个标准的输入输出流,我们可以通过 child.stdin、child.stdout 和 child.stderr 进入。

当那些流关闭之后,使用他们的子进程将会触发 close 事件。这个 close 事件和 exit 事件不同,因为多个子进程可能共享相同的 stdio 流,因此一个子进程退出不代表流关闭了。

不像在一个正常的进程中,在子进程中,stdout/stderr 流是可读的流,而 stdin 是可写的流。基本上是主进程中相反类型的。可以在这些流上使用的事件是标准的。更重要的是,在可读流上,我们可以监听 data 事件,可以得到命令的输出和在执行命令时遇到的错误:

child.stdout.on('data', (data) => {
  console.log(`child stdout: ${data}`);
});
child.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

shell 语法和 exec 函数

默认地,spawn 函数并没有创建一个 shell 去执行我们传入地命令。这使得它比 exec 函数执行稍微高效一点儿,exec 创建了个 shell。

exec 函数有另一个主要地区别。将命令的输出放到缓冲区,并且将整个输出值传递给一个回调(而不是像 spawn 那样使用流)。

如果你想要使用 shell 语法并且运行命令输出的所期望的数据比较小,建议使用 exec 函数(记住,exec 在返回结果数据之前,会在内存中缓存整个数据)。

如果期望的数据很大,那么建议使用 spawn 函数,因为数据可以被标准的 IO 对象流化(streamed)。

execFile 函数

如果你学要执行一个文件不需要使用 shell,execFile 函数就是你所需要的。它表现的和 exec 函数一样,但是不用 shell,这让它更高效一点儿。在 windows 上面,一些文件如 .bat 和 .cmd 凭它们自己不能被执行,这些文件不能被 execFile 执行,执行它们需要 exec 或者将 shell 设置为 true 的 spawn 函数。