Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

child_process 子进程

基础

源代码:lib/child_process.js

作用:衍生子进程

最基础的spawn

  • 引入

    • es6
      import { spawn } from 'child_process';
      
    • commonjs
      const spawn = require('child_process')
      

    这里spawn是异步的衍生,不会阻塞事件循环 (还有一种spawnSync是同步版本)

  • 使用例子

    • 完整指令
      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) => { }
        
  • 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}`);
  }
});