[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算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼