child_process
child_process 用于创建衍生子进程。Node 和衍生的子进程建立 stdin(标准输入), stdout(标准输出), stderr(标准错误) 管道。child_process.spawn, child_process.fork, child_process.exec, child_process.execFile 都会返回 ChildProcess 实例。ChildProcess 实例实现了 EventEmitter API,可以在子进程实例上添加事件的回调函数。进程之间可以通过事件消息系统进行互相通信。
child_process.spawn
启动一个子进程,执行命令。spawn 的接口定义: spawn(command: string, args: ReadonlyArray
- command, 需要运行的命令
- args, 运行命令的参数, 是一个字符串数组
options, 配置项
- options.cwd 子进程的工作目录
- options.env 环境变量, 使用 key, value 配置环境变量
- 等等
- 返回值 ChildProcess, 返回 ChildProcess 的实例
// 例子
const { spawn, spawnSync } = require('child_process')
const path = require('path')
const cp = spawn('ls', ['-a'], {
cwd: path.resolve(__dirname, '../Movies')
})
cp.stdout.on('data', (data) => {
console.log(`子进程输出:' ${data}`)
})
cp.on('exit', (code, signal) => {
console.log('子进程退出:', `code ${code} and signal ${signal}`)
})
输出结果:
默认情况下,子进程的标准输入、标准输出和标准错误被重定向到 ChildProcess 对象上相应的 subprocess.stdin, subprocess.stdout 和 subprocess.stderr 流。可以设置 options.stdio: inherit, 传入父进程,
const { spawn, spawnSync } = require('child_process')
const path = require('path')
const cp = spawn('ls', ['-a'], {
cwd: path.resolve(__dirname, '../Movies'),
stdio: 'inherit'
})
child_process.spawnSync
child_process.spawn 的同步版本。child_process.spawnSync 返回 Object。Object 包含了: pid(子进程pid), stdout(标准输出), stderr(标准错误) 等等。不同之处在于该函数在子进程完全关闭之前不会返回。
const { spawnSync } = require('child_process')
const path = require('path')
const obj = spawnSync('ls', ['-a'],{ cwd: path.resolve(__dirname, '../Movies') })
console.log('pid', `${obj.pid}`)
console.log('stdout', `${obj.stdout}`)
child_process.exec
创建一个衍生的 shell, 然后可以在衍生的 shell 之中执行命令。exec 实现了函数重载。第二个参数可以是配置项,或者是 callback。如果第二个参数是配置项,那么第三个参数就是 callback。
const { exec } = require('child_process')
const path = require('path')
exec('ls', (error, stdout, stderr) => {
if (error) {
console.error('error:', error);
return;
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
})
// 第二个参数可以是配置项
exec('ls -a', { cwd: path.resolve(__dirname, '../Movies'), }, (error, stdout, stderr) => {
if (error) {
console.error('error:', error);
return;
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
})
callback 的三个参数分别是错误实例(如果执行成功, error 等于 null), stdout 标准输出, stderr 标准错误。
child_process.execSync
child_process.exec 的同步版本。child_process.execSync 方法返回标准输出,不同之处在于该方法在子进程完全关闭之前不会返回。
const { execSync } = require('child_process')
const path = require('path')
const stdout = execSync('ls -a', { cwd: path.resolve(__dirname, '../Movies') })
console.log('stdout:', `${stdout}`)
child_process.exec 和 child_process.spawn 区别
spawn
- 不会创建衍生的 shell
- 流式的传输子进程产生的数据
- 没有数据大小的限制
exec
- 会创建衍生的 shell
- 最大传输 200kb 的数据
- 会缓存数据, 进程关闭后传输数据
spawn 适合巨大长时间的传输数据。exec 适合需要多次,小量的情况。
child_process.fork
child_process.fork, 用于在子进程中运行模块。child_process.fork(modulePath [, args] [, options])
- modulePath, 需要在子进程中运行的模块地址
- args, 字符串参数列表
options 配置项
- execPath, 用来创建子进程的可执行文件。我们可以通过配置这个参数,指定不同版本的 node 创建子进程。
- execArgv, 传给可执行文件的字符串参数列表。
- silent, 子进程的标准输出, 是否从父进程进行继承。默认是 false 进行继承。如果设置为 true 则直接 pipe 向子进程的 child.stdin, child.stdout 等。
- stdio, 用于配置在父进程和子进程之间建立的管道
// 子进程的代码
console.log('我是子进程')
const { fork } = require('child_process')
const { resolve } = require('path')
// 我是子进程
fork(resolve(__dirname, './cp.js'), {
silent: false
})
// 没有打印
fork(resolve(__dirname, './cp.js'), {
silent: true
})
const { fork } = require('child_process')
const { resolve } = require('path')
const cp = fork(resolve(__dirname, './cp.js'), {
silent: true
})
cp.stdout.on('data', function (data) {
// stdout 中输出: 我是子进程
console.log('stdout 中输出:', `${data}`)
})
通过 stdout 属性,可以获取到子进程输出的内容
child_process.execFile
child_process.execFile 不会创建衍生的 shell。效率要比 exec 要高。child_process.execFile(file[, args] [, options] [, callback])
- file, 可以是执行文件的名字,或者路径。
const { execFile } = require('child_process')
const { resolve } = require('path')
execFile('node', [resolve(__dirname, './cp.js')], (err, stdout, stderr) => {
if (err) {
throw err
}
console.log(`stdout: ${stdout}`)
})
child_process.exec 和 child_process.execFile 的区别
exec 内部通过调用 execFile 来实现。而 execFile 内部通过调用 spawn 来实现。
事件
ChildProcess 实例上可以监听很多事件
- close, 子进程的 stdio 流关闭时触发
- disconnect, 父进程手动调用 child.disconnect 函数时触发
- error, 产生错误时会触发
- exit, 子进程退出时触发
- message, 子进程使用 process.send 函数来传递消息时触发
const { fork } = require('child_process');
const cp = fork('./cp.js')
cp.on('close', (code, signal) => {
console.log('close 事件:', code, signal);
})
cp.on('disconnect', () => {
console.log('disconnect 事件...');
})
cp.on('error', (code, signal) => {
console.log('error 事件:', code, signal);
})
cp.on('exit', (code, signal) => {
console.log('exit 事件:', code, signal);
})
cp.on('message', (val) => {
console.log('message 事件:', val);
})
进程之间的通信
创建子进程后, 父进程与子进程之间将会创建 IPC 通道,父子进程之间通过 message 和 send 来通信。
// 父进程
const { fork } = require('child_process');
const cp = fork('./cp.js')
cp.on('message', (msg) => {
console.log('message: ' + JSON.stringify(msg))
})
// 子进程
process.send({
msg: '我是子进程'
})
父进程也可以向子进程发送消息使用 cp.send
// 父进程
const { fork } = require('child_process');
const cp = fork('./cp.js')
cp.send({
msg: '我是父进程'
})