// 文件:process.js const { spawn } = require("child_process"); const path = require("path"); // 创建子进程 let child1 = spawn("node", ["sub_process_1.js", "--port", "3000"], { cwd: path.join(__dirname, "test"), }); let child2 = spawn("node", ["sub_process_2.js"], { cwd: path.join(__dirname, "test"), }); // 读取子进程 1 写入的内容,写入子进程 2 child1.stdout.on("data", data => child2.stdout.write(data.toString));
// 文件:~test/sub_process_1.js // 获取 --port 和 3000 process.argv.slice(2).forEach(item => process.stdout.write(item));
// 文件:~test/sub_process_2.js const fs = require("fs"); // 读取主进程传递的参数并写入文件 process.stdout.on("data", data => { fs.writeFile("param.txt", data, () => { process.exit(); }); });
有一点需要注意,在子进程 2 写入文件的时候,由于主进程不知道子进程 2 什么时候写完,所以主进程会卡住,需要子进程在写入完成后调用 process.exit 方法退出子进程,子进程退出并关闭后,主进程会随之关闭。
在我们给 options 配置 stdio 时,数组内其实可以对标准输入、标准输出和错误输出分开配置,默认数组内为 pipe 时代表三者都为 pipe,分别配置看下面案例。
// 文件:process.js const { spawn } = require("spawn"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js"], { cwd: path.join(__dirname, "test"), stdio: [0, "pipe", 2] }); // world
// 文件:~test/sub_process.js console.log("hello"); console.error("world");
上面代码中对 stderr 实现了默认打印而不通信,对标准输入实现了通信,还有一种情况,如果希望子进程只是默默的执行任务,而在主进程命令窗口什么类型的输出都禁止,可以在数组中对应位置给定值 ignore,将上面案例修改如下。
// 文件:process.js const { spawn } = require("spawn"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js"], { cwd: path.join(__dirname, "test"), stdio: [0, "pipe", "ignore"] });
// 文件:~test/sub_process.js console.log("hello"); console.error("world");
这次我们发现无论标准输出和错误输出都没有生效,上面这些方式其实是不太方便的,因为输出有 stdout 和 stderr,在写法上没办法统一,可以通过下面的方式来统一。
3、标准进程通信
// 文件:process.js const { spawn } = require("spawn"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js"], { cwd: path.join(__dirname, "test"), stdio: [0, "pipe", "ignore", "ipc"] }); child.on("message", data => { console.log(data); // 回复消息给子进程 child.send("world"); // 杀死子进程 // process.kill(child.pid); }); // hello
// 文件:~test/sub_process.js // 给主进程发送消息 process.send("hello"); // 接收主进程回复的消息 process.on("message", data => { console.log(data); // 退出子进程 process.exit(); }); // world
这种方式被称为标准进程通信,通过给 options 的 stdio 数组配置 ipc,只要数组中存在 ipc 即可,一般放在数组开头或结尾,配置 ipc 后子进程通过调用自己的 send 方法发送消息给主进程,主进程中用子进程的 message 事件进行接收,也可以在主进程中接收消息的 message 事件的回调当中,通过子进程的 send 回复消息,并在子进程中用 message 事件进行接收,这样的编程方式比较统一,更贴近于开发者的意愿。
4、退出和杀死子进程
上面代码中子进程在接收到主进程的消息时直接退出,也可以在子进程发送给消息给主进程时,主进程接收到消息直接杀死子进程,代码如下。
// 文件:process.js const { spawn } = require("spawn"); const path = require("path"); // 创建子进程 let child = spawn("node", ["sub_process.js"], { cwd: path.join(__dirname, "test"), stdio: [0, "pipe", "ignore", "ipc"] }); child.on("message", data => { console.log(data); // 杀死子进程 process.kill(child.pid); }); // hello world
// 文件:~test/sub_process.js // 给主进程发送消息 process.send("hello");
从上面代码我们可以看出,杀死子进程的方法为 process.kill,由于一个主进程可能有多个子进程,所以指定要杀死的子进程需要传入子进程的 pid 属性作为 process.kill 的参数。
{% note warning %}
注意:退出子进程 process.exit 方法是在子进程中操作的,此时 process 代表子进程,杀死子进程 process.kill 是在主进程中操作的,此时 process 代表主进程。
{% endnote %}
5、独立子进程