企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # 子进程模块 我们可以使用 Node 的`child_process`模块来简单地创造子进程,子进程之间可以通过消息系统简单的通信。 <br> `child_process`模块通过在一个子进程中执行系统命令,赋予我们使用操作系统功能的能力。 <br> 我们可以控制子进程的输入流,并监听它的输出流。我们也可以修改传递给底层 OS 命令的参数,并得到任意我们想要的命令输出。举例来说,我们可以将一条命令的输出作为另一条命令的输入(正如 Linux 中那样),因为这些命令的所有输入和输出都能够使用[Node.js 流](https://medium.freecodecamp.com/node-js-streams-everything-you-need-to-know-c9141306be93)来表示。 <br> # 衍生的子进程 `spawn`函数会在一个新的进程中启动一条命令,我们可以使用它来给这条命令传递任意参数。比如,下面的代码会衍生一个执行`pwd`命令的新进程。 ~~~ const { spawn } = require('child_process'); const child = spawn('pwd'); ~~~ 我们简单地从`child_process`模块中解构`spawn`函数,然后将系统命令作为第一个参数来执行该函数。 <br> `spawn`函数(上面的`child`对象)的执行结果是一个`ChildProcess`实例,该实例实现了[EventEmitter API](https://medium.freecodecamp.com/understanding-node-js-event-driven-architecture-223292fcbc2d)。这意味着我们可以直接在这个子对象上注册事件处理器。比如,当在子进程上注册一个`exit`事件处理器时,我们可以在事件处理函数中执行一些任务: ~~~ child.on('exit', function (code, signal) { console.log('child process exited with ' + `code ${code} and signal ${signal}`); }); ~~~ 上面的处理器给出了子进程的退出`code`和`signal`,这两个变量可以用来终止子进程。子进程正常退出时`signal`变量为 null。 <br> `ChildProcess`实例上还可以注册`disconnect`、`error`、`close`和`message`事件。 * `disconnect`事件在父进程手动调用`child.disconnect`函数时触发。 * 如果进程不能被衍生或者杀死,会触发`error`事件。 * `close`事件在子进程的`stdio`流关闭时触发。 * `message`事件最为重要。它在子进程使用`process.send()`函数来传递消息时触发。这就是父/子进程间通信的原理。下面将给出一个例子。 <br> 每一个子进程还有三个标准`stdio`流,我们可以分别使用`child.stdin`、`child.stdout`和`child.stderr`来使用这三个流。 <br> 当这几个流被关闭后,使用了它们的子进程会触发`close`事件。这里的`close`事件不同于`exit`事件,因为多个子进程可能共享相同的`stdio`流,因此一个子进程退出并不意味着流已经被关闭了。 <br> 既然所有的流都是事件触发器,我们可以在归属于每个子进程的`stdio`流上监听不同的事件。不像普通的进程,在子进程中,`stdout`/`stderr`流是可读流,而`stdin`流是可写的。这基本上和主进程相反。这些流支持的事件都是标准的。最重要的是,在可读流上我们可以监听`data`事件,通过`data`事件可以得到任一命令的输出或者执行命令过程中发生的错误: ~~~ child.stdout.on('data', (data) => { console.log(`child stdout:\n${data}`); }); child.stderr.on('data', (data) => { console.error(`child stderr:\n${data}`); }); ~~~ 上述两个处理器会输出两者的日志到主进程的`stdout`和`stderr`事件上。当我们执行前面的`spawn`函数时,`pwd`命令的输出会被打印出来,并且子进程带着代码`0`退出,这表示没有错误发生。 <br> 我们可以给命令传递参数,命令由`spawn`函数执行,`spawn`函数用上了第二个参数,这是一个传递给该命令的所有参数组成的数组。比如说,为了在当前目录执行`find`命令,并带上一个`-type f`参数(用于列出所有文件),我们可以这样做: ~~~ const child = spawn('find', ['.', '-type', 'f']); ~~~ 如果这条命令的执行过程中出现错误,举个例子,如果我们在 find 一个非法的目标文件,`child.stderr``data`事件处理器将会被触发,`exit`事件处理器会报出一个退出代码`1`,这标志着出现了错误。错误的值最终取决于宿主操作系统和错误类型。 <br> 子进程中的`stdin`是一个可写流。我们可以用它给命令发送一些输入。就跟所有的可写流一样,消费输入最简单的方式是使用`pipe`函数。我们可以简单地将可读流管道化到可写流。既然主线程的`stdin`是一个可读流,我们可以将其管道化到子进程的`stdin`流。举个例子: ~~~ const { spawn } = require('child_process'); const child = spawn('wc'); process.stdin.pipe(child.stdin) child.stdout.on('data', (data) => { console.log(`child stdout:\n${data}`); }); ~~~ 在这个例子中,子进程调用`wc`命令,该命令可以统计 Linux 中的行数、单词数和字符数。我们然后将主进程的`stdin`管道化到子进程的`stdin`(一个可写流)。这个组合的结果是,我们得到了一个标准输入模式,在这个模式下,我们可以输入一些字符。当敲下`Ctrl+D`时,输入的内容将会作为`wc`命令的输入。 ![](https://box.kancloud.cn/9b13d3fbab6ee78bf6e80a0f42de715c_1000x562.png) 我们也可以将多个进程的标准输入/输出相互用管道连接,就像 Linux 命令那样。比如说,我们可以管道化`find`命令的`stdout`到`wc`命令的`stdin`,这样可以统计当前目录的所有文件。 ~~~ const { spawn } = require('child_process'); const find = spawn('find', ['.', '-type', 'f']); const wc = spawn('wc', ['-l']); find.stdout.pipe(wc.stdin); wc.stdout.on('data', (data) => { console.log(`Number of files ${data}`); }); ~~~ 我给`wc`命令添加了`-l`参数,使它只统计行数。当执行完毕,上述代码会输出当前目录下所有子目录文件的行数。 <br> <br> # 创建子进程 * 下面列出来的都是异步创建子进程的方式,每一种方式都有对应的同步版本。 * `.exec()`、`.execFile()`、`.fork()`底层都是通过`.spawn()`实现的。 * `.exec()`、`execFile()`额外提供了回调,当子进程停止的时候执行。 ## child_process.exec(command[, options][, callback]) 创建一个shell,然后在shell里执行命令。执行完成后,将stdout、stderr作为参数传入回调方法。 <br> options 参数说明: * cwd:当前工作路径。 * env:环境变量。 * encoding:编码,默认是utf8。 * shell:用来执行命令的shell,unix上默认是/bin/sh,windows上默认是cmd.exe。 * timeout:默认是0。如果timeout大于0,那么,当子进程运行超过timeout毫秒,那么,就会给进程发送killSignal指定的信号 * killSignal:默认是SIGTERM。 * uid:执行进程的uid。 * gid:执行进程的gid。 * maxBuffer:标准输出、错误输出最大允许的数据量(单位为字节),如果超出的话,子进程就会被杀死。默认是200*1024 ~~~ var exec = require('child_process').exec; // 解决windows命令编码问题 https://ask.csdn.net/questions/167560 var iconv = require('iconv-lite'); var encoding = 'cp936'; var binaryEncoding = 'binary'; // 成功的例子 exec('dir', { encoding: binaryEncoding }, function (error, stdout, stderr) { if (error) { console.log(iconv.decode('error: ' + error, encoding)); return; } console.log(iconv.decode('stdout: ' + stdout, encoding)); console.log(iconv.decode('stderr: ' + typeof stderr, encoding)); }); // 失败的例子 exec('ls hello.txt', { encoding: binaryEncoding }, function (error, stdout, stderr) { if (error) { console.log(iconv.decode('error: ' + error, encoding)); return; } console.log(iconv.decode('stdout: ' + stdout, encoding)); console.log(iconv.decode('stderr: ' + stderr, encoding)); }); ~~~ 备注: 1. 如果`timeout`大于0,那么,当子进程运行超过`timeout`毫秒,那么,就会给进程发送`killSignal`指定的信号(比如`SIGTERM`)。 2. 如果运行没有出错,那么`error`为`null`。如果运行出错,那么,`error.code`就是退出代码(exist code),`error.signal`会被设置成终止进程的信号。(比如`CTRL+C`时发送的`SIGINT`) ### 风险项 传入的命令,如果是用户输入的,有可能产生类似sql注入的风险,比如 ~~~ exec('ls hello.txt; rm -rf *', function(error, stdout, stderr){ if(error) { console.error('error: ' + error); // return; } console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); }); ~~~ <br> ## child_process.execFile(file[, args][, options][, callback]) 跟`.exec()`类似,不同点在于,没有创建一个新的shell。至少有两点影响 1. 比`child_process.exec()`效率高一些。(实际待测试) 2. 一些操作,比如I/O重定向,文件glob等不支持。 > 如果你不想用 shell 执行一个文件,那么 execFile 函数正是你想要的。它的行为跟`exec`函数一模一样,但没有使用 shell,这会让它更有效率。Windows 上,一些文件不能在它们自己之上执行,比如`.bat`或者`.cmd`文件。这些文件不能使用`execFile`执行,并且执行它们时,需要将 shell 设置为 true,且只能使用`exec`、`spawn`两者之一。 `file`:可执行文件的名字,或者路径。 ~~~ var child_process = require('child_process'); child_process.execFile('node', ['--version'], function(error, stdout, stderr){ if(error){ throw error; } console.log(stdout); }); child_process.execFile('/Users/a/.nvm/versions/node/v6.1.0/bin/node', ['--version'], function(error, stdout, stderr){ if(error){ throw error; } console.log(stdout); }); ~~~ 备注:execFile()内部最终还是通过spawn()实现的, 如果没有设置 {shell: '/bin/bash'},那么 spawm() 内部对命令的解析会有所不同,execFile('ls -al .') 会直接报错。 ~~~ var child_process = require('child_process'); var execFile = child_process.execFile; var exec = child_process.exec; exec('ls -al .', function(error, stdout, stderr){ if(error){ throw error; } console.log(stdout); }); execFile('ls -al .', {shell: '/bin/bash'}, function(error, stdout, stderr){ if(error){ throw error; } console.log(stdout); }); ~~~ <br> ## child_process.fork(modulePath[, args][, options]) `fork`函数是`spawn`函数针对衍生 node 进程的一个变种。`spawn`和`fork`最大的区别在于,使用`fork`时,通信频道建立于子进程,因此我们可以在 fork 出来的进程上使用`send`函数,这些进程上有个全局`process`对象,可以用于父进程和 fork 进程之间传递消息。这个函数通过`EventEmitter`模块接口实现。这里有个例子: 参数说明:(重复的参数说明就不在这里列举) * `execPath`: 用来创建子进程的可执行文件,默认是`/usr/local/bin/node`。也就是说,你可通过`execPath`来指定具体的node可执行文件路径。(比如多个node版本) * `execArgv`: 传给可执行文件的字符串参数列表。默认是`process.execArgv`,跟父进程保持一致。 * `silent`: 默认是`false`,即子进程的`stdio`从父进程继承。如果是`true`,则直接`pipe`向子进程的`child.stdin`、`child.stdout`等。 * `stdio`: 如果声明了`stdio`,则会覆盖`silent`选项的设置。 ### 例子一 这里有个 HTTP 服务器处理两个端点。一个端点(下面的`/compute`)计算密集,会花好几秒种完成。我们可以用一个长循环来模拟: ~~~ const http = require('http'); const longComputation = () => { let sum = 0; for (let i = 0; i < 1e9; i++) { sum += i; }; return sum; }; const server = http.createServer(); server.on('request', (req, res) => { if (req.url === '/compute') { const sum = longComputation(); return res.end(`Sum is ${sum}`); } else { res.end('Ok') } }); server.listen(3000); ~~~ 这段程序有个比较大的问题:当`/compute`端点被请求,服务器不能处理其他请求,因为长循环导致事件循环处于繁忙状态。 这个问题有一些解决之道,这取决于耗时长运算的性质。但针对所有运算都适用的解决方法是,用`fork`将计算过程移动到另一个进程。 我们首先移动整个`longComputation`函数到它自己的文件,并在主进程通过消息发出通知时,在文件中调用这个函数: 一个新的`compute.js`文件中: ~~~ const longComputation = () => { let sum = 0; for (let i = 0; i < 1e9; i++) { sum += i; }; return sum; }; process.on('message', (msg) => { const sum = longComputation(); process.send(sum); }); ~~~ 现在,我们可以fork`compute.js`文件,并用消息接口实现服务器和复刻进程的消息通信,而不是在主进程事件循环中执行耗时操作。 ~~~ const http = require('http'); const { fork } = require('child_process'); const server = http.createServer(); server.on('request', (req, res) => { if (req.url === '/compute') { const compute = fork('compute.js'); compute.send('start'); compute.on('message', sum => { res.end(`Sum is ${sum}`); }); } else { res.end('Ok') } }); server.listen(3000); ~~~ 上面的代码中,当`/compute`来了一个请求,我们可以简单地发送一条消息给复刻进程,来启动执行耗时运算。主进程的事件循环并不会阻塞。 一旦复刻进程执行完耗时操作,它可以用`process.send`将结果发回给父进程。 在父进程中,我们在 fork 的子进程本身上监听`message`事件。当该事件触发,我们会得到一个准备好的`sum`值,并通过 HTTP 发送给请求。 上面的代码,当然,我们可以 fork 的进程数是有限的。但执行这段代码时,HTTP 请求耗时运算的端点,主服务器根本不会阻塞,并且还可以接受更多的请求。 ### 例子二 **parent.js** ~~~ var child_process = require('child_process'); // 例子一:会打印出 output from the child // 默认情况,silent 为 false,子进程的 stdout 等 // 从父进程继承 child_process.fork('./child.js', { silent: false }); // 例子二:不会打印出 output from the silent child // silent 为 true,子进程的 stdout 等 // pipe 向父进程 child_process.fork('./silentChild.js', { silent: true }); // 例子三:打印出 output from another silent child var child = child_process.fork('./anotherSilentChild.js', { silent: true }); child.stdout.setEncoding('utf8'); child.stdout.on('data', function(data){ console.log(data); });复制代码 ~~~ **child.js** ~~~ console.log('output from the child');复制代码 ~~~ **silentChild.js** ~~~ console.log('output from the silent child');复制代码 ~~~ **anotherSilentChild.js** ~~~ console.log('output from another silent child');复制代码 ~~~ <br> ### 例子三:ipc **parent.js** ~~~ var child_process = require('child_process'); var child = child_process.fork('./child.js'); child.on('message', function(m){ console.log('message from child: ' + JSON.stringify(m)); }); child.send({from: 'parent'});复制代码 ~~~ **child.js** ~~~ process.on('message', function(m){ console.log('message from parent: ' + JSON.stringify(m)); }); process.send({from: 'child'});复制代码 ~~~ 运行结果 ~~~ ➜ ipc git:(master) ✗ node parent.js message from child: {"from":"child"} message from parent: {"from":"parent"} ~~~ ### 例子四:execArgv 首先,process.execArgv的定义,参考[这里](https://link.juejin.im?target=https%3A%2F%2Fnodejs.org%2Fapi%2Fprocess.html%23process_process_execargv)。设置`execArgv`的目的一般在于,让子进程跟父进程保持相同的执行环境。 比如,父进程指定了`--harmony`,如果子进程没有指定,那么就要跪了。 parent.js ~~~ var child_process = require('child_process'); console.log('parent execArgv: ' + process.execArgv); child_process.fork('./child.js', { execArgv: process.execArgv });复制代码 ~~~ child.js ~~~ console.log('child execArgv: ' + process.execArgv);复制代码 ~~~ 运行结果 ~~~ ➜ execArgv git:(master) ✗ node --harmony parent.js parent execArgv: --harmony child execArgv: --harmony ~~~ ## child_process.spawn(command[, args][, options]) `command`:要执行的命令 options参数说明: * `argv0`:\[String\] 这货比较诡异,在uninx、windows上表现不一样。有需要再深究。 * `stdio`:\[Array\] | \[String\] 子进程的stdio。参考[这里](https://link.juejin.im?target=https%3A%2F%2Fnodejs.org%2Fapi%2Fchild_process.html%23child_process_options_stdio) * `detached`:\[Boolean\] 让子进程独立于父进程之外运行。同样在不同平台上表现有差异,具体参考[这里](https://link.juejin.im?target=https%3A%2F%2Fnodejs.org%2Fapi%2Fchild_process.html%23child_process_options_detached) * `shell`:\[Boolean\] | \[String\] 如果是`true`,在shell里运行程序。默认是`false`。(很有用,比如 可以通过 /bin/sh -c xxx 来实现 .exec() 这样的效果) 例子1:基础例子 ~~~ var spawn = require('child_process').spawn; var ls = spawn('ls', ['-al']); ls.stdout.on('data', function(data){ console.log('data from child: ' + data); }); ls.stderr.on('data', function(data){ console.log('error from child: ' + data); }); ls.on('close', function(code){ console.log('child exists with code: ' + code); });复制代码 ~~~ 例子2:声明stdio ~~~ var spawn = require('child_process').spawn; var ls = spawn('ls', ['-al'], { stdio: 'inherit' }); ls.on('close', function(code){ console.log('child exists with code: ' + code); });复制代码 ~~~ 例子3:声明使用shell ~~~ var spawn = require('child_process').spawn; // 运行 echo "hello nodejs" | wc var ls = spawn('bash', ['-c', 'echo "hello nodejs" | wc'], { stdio: 'inherit', shell: true }); ls.on('close', function(code){ console.log('child exists with code: ' + code); });复制代码 ~~~ 例子4:错误处理,包含两种场景,这两种场景有不同的处理方式。 * 场景1:命令本身不存在,创建子进程报错。 * 场景2:命令存在,但运行过程报错。 ~~~ var spawn = require('child_process').spawn; var child = spawn('bad_command'); child.on('error', (err) => { console.log('Failed to start child process 1.'); }); var child2 = spawn('ls', ['nonexistFile']); child2.stderr.on('data', function(data){ console.log('Error msg from process 2: ' + data); }); child2.on('error', (err) => { console.log('Failed to start child process 2.'); });复制代码 ~~~ 运行结果如下。 ~~~ ➜ spawn git:(master) ✗ node error/error.js Failed to start child process 1. Error msg from process 2: ls: nonexistFile: No such file or directory复制代码 ~~~ 例子5:echo "hello nodejs" | grep "nodejs" ~~~ // echo "hello nodejs" | grep "nodejs" var child_process = require('child_process'); var echo = child_process.spawn('echo', ['hello nodejs']); var grep = child_process.spawn('grep', ['nodejs']); grep.stdout.setEncoding('utf8'); echo.stdout.on('data', function(data){ grep.stdin.write(data); }); echo.on('close', function(code){ if(code!==0){ console.log('echo exists with code: ' + code); } grep.stdin.end(); }); grep.stdout.on('data', function(data){ console.log('grep: ' + data); }); grep.on('close', function(code){ if(code!==0){ console.log('grep exists with code: ' + code); } });复制代码 ~~~ 运行结果: ~~~ ➜ spawn git:(master) ✗ node pipe/pipe.js grep: hello nodejs复制代码 ~~~ # 关于options.stdio 默认值:\['pipe', 'pipe', 'pipe'\],这意味着: 1. child.stdin、child.stdout 不是`undefined` 2. 可以通过监听 `data` 事件,来获取数据。 ## 基础例子 ~~~ var spawn = require('child_process').spawn; var ls = spawn('ls', ['-al']); ls.stdout.on('data', function(data){ console.log('data from child: ' + data); }); ls.on('close', function(code){ console.log('child exists with code: ' + code); });复制代码 ~~~ ## 通过child.stdin.write()写入 ~~~ var spawn = require('child_process').spawn; var grep = spawn('grep', ['nodejs']); setTimeout(function(){ grep.stdin.write('hello nodejs \n hello javascript'); grep.stdin.end(); }, 2000); grep.stdout.on('data', function(data){ console.log('data from grep: ' + data); }); grep.on('close', function(code){ console.log('grep exists with code: ' + code); }); ~~~ <br> <br> # options.detached 在 Windows 上,设置 options.detached 为 true 可以使子进程在父进程退出后继续运行。 子进程有自己的控制台窗口。 一旦启用一个子进程,它将不能被禁用。 <br> 在非 Windows 平台上,如果 options.detached 设为 true,则子进程会成为新的进程组和会话的领导者。 子进程在父进程退出后可以继续运行,不管它们是否被分离。 <br> 默认情况下,父进程会等待被分离的子进程退出。 为了防止父进程等待 subprocess,可以使用 subprocess.unref()。 这样做会导致父进程的事件循环不包含子进程的引用计数,使得父进程独立于子进程退出,除非子进程和父进程之间建立了一个 IPC 信道。 <br> 当使用 detached 选项来启动一个长期运行的进程时,该进程不会在父进程退出后保持在后台运行,除非指定一个不连接到父进程的 stdio 配置。 如果父进程的 stdio 是继承的,则子进程会保持连接到控制终端。 <br> ## 默认情况:父进程等待子进程结束。 子进程。可以看到,有个定时器一直在跑 ~~~ var times = 0; setInterval(function(){ console.log(++times); }, 1000);复制代码 ~~~ 运行下面代码,会发现父进程一直hold着不退出。 ~~~ var child_process = require('child_process'); child_process.spawn('node', ['child.js'], { // stdio: 'inherit' }); ~~~ <br> ## 通过child.unref()让父进程退出 调用`child.unref()`,将子进程从父进程的事件循环中剔除。于是父进程可以愉快的退出。这里有几个要点 1. 调用`child.unref()` 2. 设置`detached`为`true` 3. 设置`stdio`为`ignore`(这点容易忘) ~~~ var child_process = require('child_process'); var child = child_process.spawn('node', ['child.js'], { detached: true, stdio: 'ignore' // 备注:如果不置为 ignore,那么 父进程还是不会退出 // stdio: 'inherit' }); child.unref();复制代码 ~~~ ## 将`stdio`重定向到文件 除了直接将stdio设置为`ignore`,还可以将它重定向到本地的文件。 ~~~ var child_process = require('child_process'); var fs = require('fs'); var out = fs.openSync('./out.log', 'a'); var err = fs.openSync('./err.log', 'a'); var child = child_process.spawn('node', ['child.js'], { detached: true, stdio: ['ignore', out, err] }); child.unref(); ~~~ <br> <br> # 在 Windows 上衍生`.bat`和`.cmd`文件 [`child_process.exec()`](http://nodejs.cn/s/pkpJMy)和[`child_process.execFile()`](http://nodejs.cn/s/N6uK8q)之间的重要区别可能因平台而异。 在 Unix 类型的操作系统(Unix、Linux、macOS)上,[`child_process.execFile()`](http://nodejs.cn/s/N6uK8q)可以更高效,因为默认情况下它不会衍生 shell。 但是在 Windows 上,`.bat`和`.cmd`文件在没有终端的情况下不能自行执行,因此无法使用[`child_process.execFile()`](http://nodejs.cn/s/N6uK8q)启动。 在 Windows 上运行时,可以使用带有`shell`选项集的[`child_process.spawn()`](http://nodejs.cn/s/CKoDGf)、或使用[`child_process.exec()`](http://nodejs.cn/s/pkpJMy)或通过衍生`cmd.exe`并将`.bat`或`.cmd`文件作为参数传入(也就是`shell`选项和[`child_process.exec()`](http://nodejs.cn/s/pkpJMy)所做的)。 在任何情况下,如果脚本文件名包含空格,则需要加上引号。 ~~~js // 仅限 Windows 系统。 const { spawn } = require('child_process'); const bat = spawn('cmd.exe', ['/c', 'my.bat']); bat.stdout.on('data', (data) => { console.log(data.toString()); }); bat.stderr.on('data', (data) => { console.log(data.toString()); }); bat.on('exit', (code) => { console.log(`子进程退出码:${code}`); }); ~~~ ~~~js // 或: const { exec } = require('child_process'); exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); return; } console.log(stdout); }); // 文件名中包含空格的脚本: const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true }); // 或: exec('"my script.cmd" a b', (err, stdout, stderr) => { // ... }); ~~~ # 代码运行次序的问题 **p.js** ~~~ const cp = require('child_process'); const n = cp.fork(`${__dirname}/sub.js`); console.log('1'); n.on('message', (m) => { console.log('PARENT got message:', m); }); console.log('2'); n.send({ hello: 'world' }); console.log('3'); ~~~ **sub.js** ~~~ console.log('4'); process.on('message', (m) => { console.log('CHILD got message:', m); }); process.send({ foo: 'bar' }); console.log('5');复制代码 ~~~ 运行`node p.js`,打印出来的内容如下 ~~~ ➜ ch node p.js 1 2 3 4 5 PARENT got message: { foo: 'bar' } CHILD got message: { hello: 'world' }复制代码 ~~~ 再来个例子 ~~~ // p2.js var fork = require('child_process').fork; console.log('p: 1'); fork('./c2.js'); console.log('p: 2'); // 从测试结果来看,同样是70ms,有的时候,定时器回调比子进程先执行,有的时候比子进程慢执行。 const t = 70; setTimeout(function(){ console.log('p: 3 in %s', t); }, t); // c2.js console.log('c: 1'); ~~~ <br> # 参考资料 [Nodejs进阶:如何玩转子进程(child\_process)](https://juejin.im/post/5848ee3c8e450a006aad306b) [Node.js 子进程:你应该知道的一切](https://github.com/xitu/gold-miner/blob/master/TODO/node-js-child-processes-everything-you-need-to-know.md) [深入理解Node.js 中的进程与线程](https://juejin.im/post/5d43017be51d4561f40adcf9) [Nodejs cluster模块深入探究](https://segmentfault.com/a/1190000010260600) [nodejs中的子进程,深入解析child_process模块和cluster模块](https://segmentfault.com/a/1190000016169207)