学习使用Node.js子进程

278 阅读4分钟

Node.js子进程入门

在本教程中,我们将讨论如何使用子进程来启动另一个进程,以避免执行程序时出现延迟。

简介

Node.js程序在执行时是在单线程上运行的。执行数以百万计的进程会导致延迟/阻塞,因此需要一个解决方案。解决这种延迟的方法之一是使用Node.js的子进程。

前提条件

本教程需要JavaScript和Node.js的基本知识。

开始学习

Node.js是单线程的,这意味着无论你的服务器多么强大,它在给定时间内只支持几个负载。这就是子进程的作用。它们有助于解决这些延迟问题,从而提高程序的总体性能。

子进程模块

子进程模块有几种方法,我们可以用它们来创建其他进程。

在本教程中,我们将介绍以下两个主要方法。

  1. child_process.exec()
  2. child_process.spoon()

使用exec() 方法创建子进程

child_process.exec() 方法创建一个新的shell进程。然后它使用这个shell来执行命令。这些执行命令的输出被保存在一个内存缓冲区中。

然后,这个输出可以通过传递给exec() 方法的回调函数来访问。让我们看一个例子。

注意:在本教程中,我们使用Node.js强大的[REPL](https://nodejs.dev/learn/how-to-use-the-nodejs-repl) 命令行工具来运行/测试我们的结果,但你可以根据你的要求自由创建文件。

通过在终端运行以下命令启动你的REPL


$ node

输出。

$ node
Welcome to Node.js v13.14.0.
Type ".help" for more information.
> 

接下来,让我们通过运行以下命令导入child_process 模块。

...................
> const {exec} = require('child_process');
undefined
> 

让我们继续,并记录到我们的控制台exec 常数。

输出。

> const {exec} = require(`child_process`);
undefined
> console.log(exec);
[Function: exec]   //output
undefined
> 

你注意到,exec 常量的类型是Function 对象。

现在让我们看一下child_process.exec()方法的语法。

child_process.exec(command[, options][, callback])

这个方法需要2个参数,command 作为第一个参数,有可选的选项和一个callback 函数。command 参数是字符串类型的,期望执行一条命令,例如,一条列出你的目录文件的命令。

options 是一个对象,它需要几个其他的方法,例如,获得子进程的当前工作目录process.cwd()callback 是一个函数,当一个进程被终止时应该被调用。

它接受以下3个参数。

  • error--返回一个类型为Error 的错误。
  • stdout|-返回一个字符串或一个缓冲区,处理二进制数据。
  • stderr|- 返回一个字符串/缓冲区。

现在我们已经介绍了这个方法的基本工作原理,让我们看看一个工作实例。

在你的REPL ,添加以下脚本来执行ls 命令,列出你当前目录下的文件夹和文件。

> exec('ls -lh', (error, stdout, stderr) => {
...   if (err) {
.....     console.error(err);
.....     return;
.....   }
... 
...   if (stderr) {
...     console.error(stderr);
...     return;
...   }
... 
...   console.log(stdout);
... });
>  
undefined
> 

输出。

> stdout:
total 8.0K
-rwxrwxrwx 1 root       root        612 Apr  2 11:57 index.nginx-debian.html
lrwxrwxrwx 1 root       root         21 Apr  2 12:54 phpmyadmin -> /usr/share/phpmyadmin
drwxrwxr-x 5 wen wen 4.0K Apr  2 12:24 project

从上面的脚本中:
我们首先导入child_process 模块,使用一种极其简洁的语法,即[JavaScript的结构化]。

child_process.exec() 预计有2个参数,命令和回调函数。回调函数需要3个参数: , , 和 。error stdout stderr

在成功执行时,error 返回null ,否则返回一个Error 的实例。

stdoutstderr 包含子进程的输出,在UTF-8中解码,如上面的输出所示。

使用'child_process.spoon()'创建子进程

child_process.exec() ,这个方法需要使用流,即数据是通过流API返回的。

因此,为了获得所需的结果,我们要监听流事件。

让我们看一个例子。

在你的REPL ,运行以下命令。


Type ".help" for more information.
> const { spawn } = require('child_process');
undefined
> 
> const spawned_child = spawn('cat', ['child.js']);
undefined
> 

在上面的脚本中,我们首先导入child_process 模块。结果被分配给一个去结构化的常量,spawn

然后我们通过调用spawn() 方法创建一个子进程。这个方法接受两个参数,命令和可选的选项。

产生的输出被分配给一个spawned_child 常量。

同样重要的是要注意,spawn函数的调用将总是返回ChildProcess实例。

$ node
Welcome to Node.js v13.14.0.
Type ".help" for more information.
> const fs = require('fs')
undefined
> const { spawn } = require('child_process')
undefined
> const filename ='child.js'
undefined
> 
> fs.watch(filename, () => {
...   const ls = spawn('ls', ['-lh', filename])
...   ls.stdout.pipe(process.stdout)
... })
<ref *1> FSWatcher {
  _events: [Object: null prototype] { change: [Function (anonymous)] },
  _eventsCount: 1,
  _maxListeners: undefined,
  _handle: FSEvent {
    onchange: [Function (anonymous)],
    [Symbol(owner_symbol)]: [Circular *1]
  },
  [Symbol(kCapture)]: false
}

在上面的脚本中,我们已经创建了一个文件child.js 。我们正在观察这个文件的变化,即编辑或删除。每当有变化时,我们就运行命令ls -lh

然后,我们将主进程的输出从子进程中引出。

请注意,如果我们不把主进程的结果通过管道输出,就不会有输出显示。

总结

在本教程中,我们已经看到了如何使用child_process.exec()child_process.spawn() 来创建子进程。

还有其他一些方法,如child_process.fork() ,它是child_process.spawn() 方法的一个变种。