[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`命令的输入。

我们也可以将多个进程的标准输入/输出相互用管道连接,就像 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)
- 第一部分 HTML
- meta
- meta标签
- HTML5
- 2.1 语义
- 2.2 通信
- 2.3 离线&存储
- 2.4 多媒体
- 2.5 3D,图像&效果
- 2.6 性能&集成
- 2.7 设备访问
- SEO
- Canvas
- 压缩图片
- 制作圆角矩形
- 全局属性
- 第二部分 CSS
- CSS原理
- 层叠上下文(stacking context)
- 外边距合并
- 块状格式化上下文(BFC)
- 盒模型
- important
- 样式继承
- 层叠
- 属性值处理流程
- 分辨率
- 视口
- CSS API
- grid(未完成)
- flex
- 选择器
- 3D
- Matrix
- AT规则
- line-height 和 vertical-align
- CSS技术
- 居中
- 响应式布局
- 兼容性
- 移动端适配方案
- CSS应用
- CSS Modules(未完成)
- 分层
- 面向对象CSS(未完成)
- 布局
- 三列布局
- 单列等宽,其他多列自适应均匀
- 多列等高
- 圣杯布局
- 双飞翼布局
- 瀑布流
- 1px问题
- 适配iPhoneX
- 横屏适配
- 图片模糊问题
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 内存空间
- 作用域
- 执行上下文栈
- 变量对象
- 作用域链
- this
- 类型转换
- 闭包(未完成)
- 原型、面向对象
- class和extend
- 继承
- new
- DOM
- Event Loop
- 垃圾回收机制
- 内存泄漏
- 数值存储
- 连等赋值
- 基本类型
- 堆栈溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍历对象属性
- 宽度、高度
- performance
- 位运算
- tostring( ) 与 valueOf( )方法
- JavaScript技术
- 错误
- 异常处理
- 存储
- Cookie与Session
- ES6(未完成)
- Babel转码
- let和const命令
- 变量的解构赋值
- 字符串的扩展
- 正则的扩展
- 数值的扩展
- 数组的扩展
- 函数的扩展
- 对象的扩展
- Symbol
- Set 和 Map 数据结构
- proxy
- Reflect
- module
- AJAX
- ES5
- 严格模式
- JSON
- 数组方法
- 对象方法
- 函数方法
- 服务端推送(未完成)
- JavaScript应用
- 复杂判断
- 3D 全景图
- 重载
- 上传(未完成)
- 上传方式
- 文件格式
- 渲染大量数据
- 图片裁剪
- 斐波那契数列
- 编码
- 数组去重
- 浅拷贝、深拷贝
- instanceof
- 模拟 new
- 防抖
- 节流
- 数组扁平化
- sleep函数
- 模拟bind
- 柯里化
- 零碎知识点
- 第四部分 进阶
- 计算机原理
- 数据结构(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 搜索算法
- 动态规划
- 二叉树
- 浏览器
- 浏览器结构
- 浏览器工作原理
- HTML解析
- CSS解析
- 渲染树构建
- 布局(Layout)
- 渲染
- 浏览器输入 URL 后发生了什么
- 跨域
- 缓存机制
- reflow(回流)和repaint(重绘)
- 渲染层合并
- 编译(未完成)
- Babel
- 设计模式(未完成)
- 函数式编程(未完成)
- 正则表达式(未完成)
- 性能
- 性能分析
- 性能指标
- 首屏加载
- 优化
- 浏览器层面
- HTTP层面
- 代码层面
- 构建层面
- 移动端首屏优化
- 服务器层面
- bigpipe
- 构建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack优化
- Webpack原理
- 实现loader
- 实现plugin
- tapable
- Webpack打包后代码
- rollup.js
- parcel
- 模块化
- ESM
- 安全
- XSS
- CSRF
- 点击劫持
- 中间人攻击
- 密码存储
- 测试(未完成)
- 单元测试
- E2E测试
- 框架测试
- 样式回归测试
- 异步测试
- 自动化测试
- PWA
- PWA官网
- web app manifest
- service worker
- app install banners
- 调试PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 饿了么整理
- 样式
- 技巧
- Vue音乐播放器
- Vue源码
- Virtual Dom
- computed原理
- 数组绑定原理
- 双向绑定
- nextTick
- keep-alive
- 导航守卫
- 组件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 动画(未完成)
- 异常监控、收集(未完成)
- 数据采集
- Sentry
- 贝塞尔曲线
- 视频
- 服务端渲染
- 服务端渲染的利与弊
- Vue SSR
- React SSR
- 客户端
- 离线包
- 第五部分 网络
- 五层协议
- TCP
- UDP
- HTTP
- 方法
- 首部
- 状态码
- 持久连接
- TLS
- content-type
- Redirect
- CSP
- 请求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服务端
- Linux
- Linux命令
- 权限
- XAMPP
- Node.js
- 安装
- Node模块化
- 设置环境变量
- Node的event loop
- 进程
- 全局对象
- 异步IO与事件驱动
- 文件系统
- Node错误处理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服务
- 负载均衡
- 获取用户IP
- 解决跨域
- 适配PC与移动环境
- 简单的访问限制
- 页面内容修改
- 图片处理
- 合并请求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自动化(未完成)
- docker
- 创建CLI
- 持续集成
- 持续交付
- 持续部署
- Jenkins
- 部署与发布
- 远程登录服务器
- 增强服务器安全等级
- 搭建 Nodejs 生产环境
- 配置 Nginx 实现反向代理
- 管理域名解析
- 配置 PM2 一键部署
- 发布上线
- 部署HTTPS
- Node 应用
- 爬虫(未完成)
- 例子
- 反爬虫
- 中间件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源码
- app.js
- config.js
- 消息队列
- RPC
- 性能优化
- 第七部分 总结
- Web服务器
- 目录结构
- 依赖
- 功能
- 代码片段
- 整理
- 知识清单、博客
- 项目、组件、库
- Node代码
- 面试必考
- 91算法
- 第八部分 工作代码总结
- 样式代码
- 框架代码
- 组件代码
- 功能代码
- 通用代码