child_process 子进程
基础
源代码:lib/child_process.js
作用:衍生子进程
最基础的spawn
-
引入
- es6
import { spawn } from 'child_process'; - commonjs
const spawn = require('child_process')
这里spawn是异步的衍生,不会阻塞事件循环 (还有一种spawnSync是同步版本)
- es6
-
使用例子
- 完整指令
child_process.spawn(command[, args][, options])
衍生一个
执行ls命令的子进程,后面的数组是命令的参数const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code ${code}`); }); - 完整指令
创建异步进程
基于spawn,封装有一些更高级的方法:
这里面每个方法都返回 ChildProcess 实例
-
exec()- 衍生shell并执行命令
- 完成后,将 stdout 和 stderr 传给回调函数
- 有同步版本execSync
- 完整指令
child_process.exec(command[, options][, callback])- callback
(error, stdout, stderr) => { }
- callback
-
execFile()- 不会衍生shell,更高效地执行命令
- 多用于Unix类型系统,win不行是因为.bat做不到无终端执行
- 完整指令
child_process.execFile(file[,args][,options][,callback])
-
fork()-
是spawn的特例,专门用来衍生新的nodejs进程
-
默认用父进程的 process.execPath来衍生
- 可自定义:
- 更改options 对象中的 execPath
- 这样会使用环境变量 NODE_CHANNEL_FD 标识的文件描述符与父进程通信
- 可自定义:
-
额外建立IPC信道(允许父子进程通信)
-
注意、
- 衍生的 nodejs 子进程独立于父进程,除了两者间的IPC信道。
- 每个进程都有自己的内存和 V8 实例
- 由于需要额外资源分配,不建议衍生大量子进程
-
完整指令
child_process.fork(modulePath[, args][, options])- modulePath 要在子进程中运行的模块
-
自我调用的例子 这里启用了 signal 选项,使用了 AbortController 进行中止 (??很奇怪,延迟执行反而不抛出err了,是不是因为子进程提前结束)
import { fork } from 'child_process'; import path from 'path'; import { setTimeout as delay } from 'timers/promises'; const __dirname = path.resolve(); const __filename = path.join(__dirname,'.','buffer.mjs') if (process.argv[2] === 'child') { setTimeout(() => { console.log(`Hello from ${process.argv[2]}!`); }, 1000); } else { const controller = new AbortController(); const { signal } = controller; const child = fork(__filename, ['child'], { signal }); child.on('error', (err) => { // 如果控制器中止,则err为AbortError抛出 console.log(err) }); await delay(2000) controller.abort(); // 停止子进程 } -
封装的fork用上ipc通信等价于
const child = spawn('node', ['./ipc-child.js'], { stdio: [null, null, null, 'ipc'] });
-
ChildProcess 类
事件
close exit error
方法
.kill([signal])
发送消息
主进程
import { fork } from 'child_process';
import path from 'path';
const __dirname = path.resolve();
const __filename = path.join(__dirname,'.','buffer.mjs')
const n = fork(`${__dirname}/sub.mjs`);
n.on('message', (m) => {
console.log('PARENT got message:', m);
});
// 引起子进程打印:CHILD got message: { hello: 'world' }
n.send({ hello: 'world' });
子进程
process.on('message', (m) => {
console.log('CHILD got message:', m);
});
// 引起父进程打印:PARENT got message: { foo: 'bar', baz: null }
process.send({ foo: 'bar', baz: NaN });
???这里很奇怪,为什么只打印了PARENT got ,子进程的监听貌似无效
属性
pid
connected
exitCode
killed
util.promisify
作用:转化为async/await风格的异步调用形式
例子
import util from 'util';
import { exec } from 'child_process';
const exec_p = util.promisify(exec);
async function lsExample() {
const { stdout, stderr } = await exec_p('ls');
console.log('stdout:', stdout);
console.error('stderr:', stderr);
}
lsExample();
关于spawn的一个精细化例子
想要子进程执行ps ax | grep ssh
模拟管道
const { spawn } = require('child_process');
const ps = spawn('ps', ['ax']);
const grep = spawn('grep', ['ssh']);
ps.stdout.on('data', (data) => {
grep.stdin.write(data);
});
ps.stderr.on('data', (data) => {
console.error(`ps stderr: ${data}`);
});
ps.on('close', (code) => {
if (code !== 0) {
console.log(`ps process exited with code ${code}`);
}
grep.stdin.end();
});
grep.stdout.on('data', (data) => {
console.log(data.toString());
});
grep.stderr.on('data', (data) => {
console.error(`grep stderr: ${data}`);
});
grep.on('close', (code) => {
if (code !== 0) {
console.log(`grep process exited with code ${code}`);
}
});