Node.JS事件和流


传统上,在Web服务器中,通过读取和写入来处理文件形式的数据会消耗更多的内存,因为这些处理方法需要在每次必须读取或写入该文件时加载文件。这被认为是浪费资源。想想看,就可扩展性和大数据而言,如果我们浪费资源,我们将会妥协很多。 Node.js异步特性为我们提供了两个合适的选项,并提供了一个消耗较少资源的数据流,因为Node.js基于非阻塞异步模型。他们正在发射事件和流。在本节中,我们将介绍它们。

EventEmitter类

EventEmitters是Node.js中事件驱动编程或异步编程体系结构背后的核心思想之一。 EventEmitter是一个对象,在Node.js中,发出事件的任何对象都是EventEmitter类的实例。什么是活动? Node.js程序采取的每个操作(例如启动Web服务器以及在没有剩余请求执行时关闭终止程序)都被视为两个单独的事件。

要访问Node.js程序中的EventEmitter类,您必须要求Node.js API中的events模块。为了创建对象,我们通过调用其构造函数来创建EventEmitter类的实例。

const events = require('events');
const eventEmitter = new events.EventEmitter();

或者您可以直接要求访问EventEmitter子类,如下所示:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

EventEmitter类提供各种预定义的方法来处理事件。这些方法是.on.emit.error 。从函数发出事件可以触发一个可以被任何其他JavaScript函数访问的回调函数。这就像广播。

触发事件的能力可以通过以下语法完成:

eventEmitter.emit(eventName, optionalData);

附加侦听器功能和定义特定事件名称的能力由.on完成。

eventEmitter.emit(eventName, callback);

我们将通过一个例子模仿我们刚刚学到的新功能。创建一个名为eventemitter.js的新文件并粘贴以下代码:

const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

const callback = () => {
  console.log('callback runs');
};

eventEmitter.on('invoke', callback);

eventEmitter.emit('invoke');
eventEmitter.emit('invoke');

现在使用node命令运行上面的示例,您必须获得以下输出。

callback runs
callback runs

我们通过创建一个eventEmitter实例,通过它我们可以访问开始.on了方法。 .on方法通过定义名称invoke来添加事件,我们稍后在.emit中使用它来调用触发与之关联的回调函数。

EventEmitter类提供了另一个名为.once 。此方法仅在发出事件时首次调用与事件关联的回调函数。考虑下面的例子。

const EventEmitter = require('events');

const eventEmitter = new EventEmitter();

const callback = () => {
  console.log('callback runs');
};

eventEmitter.once('invoke', callback);

eventEmitter.emit('invoke');
eventEmitter.emit('invoke');

输出

callback runs

.once跟踪事件的触发时间以及它们被触发的次数,与.on方法不同,它不会像这样跟踪。这是两者之间的主要区别。

了解流

Node.js提供了另一种处理数据的方法,而不是消耗大量的内存资源并使其具有成本效益。这就是溪流。基本上,流可以让您从一个源读取数据并放入目标。 Streams以块的形式处理数据而不是一次性处理数据,因此它们适合处理大型数据集。许多内置的Node.js模块使用流引擎。例如,HTTP请求和响应,TCP套接字,zlib,加密,fs读写流等。

流的类型

在Node.js中有四种类型的流:

  • 可读
  • 可写
  • 复式
  • 转变

其中最常见的是可读和可写流。可读流用于从源读取数据,可写流用于执行该数据到目的地的写操作。双工流可用于执行读取和写入操作。 Transform是Duplex流的超集,唯一的区别在于,在读取或写入时可以修改数据。

流事件的类型

所有这些流类型都是EventEmitter类的实例,这意味着它们发出读写事件。每种类型的流都可以发出以下事件。

  • data:当可读流可读取数据时触发此事件
  • 错误:读取或写入数据时出错,将触发此事件
  • 结束:当没有数据要读取时,会触发此事件

可读流

可读流可让您从源读取数据。此源可以是缓冲区,文件等。首先,创建一个文件简单文本文件,我们将使用该流从中读取数据。

I am Text file that contains data.

现在,创建一个名为read.js的新文件,该文件将实现使用可读流从此文本文件中读取数据的功能。

const fs = require('fs');
const readableStream = fs.createReadStream('abc.txt');
let data = '';

readableStream.on('data', function(chunk) {
  data += chunk;
});

readableStream.on('end', function() {
  console.log(data);
});

如果运行上述程序,您将获得以下输出:

$ node test.js
 I am Text file that contains data.

这是我们在文本文件中写的内容。要使用流读取数据,我们使用一个名为createReadStream()的函数来处理文件系统模块fs

当可读流没有剩余数据要读取时,它会自动结束回调功能。 .on方法是我们在EventEmitter类的前一节中学到的。这表示流在后台使用EventEmitter类。

可写流

可写流用于将数据写入或插入或附加到目标。与可读流一样,它们也由fs模块提供。创建一个名为wrtte.js的新文件,其中将使用可读流从源读取数据并通过创建新的.txt文件将其写入目标。

const fs = require('fs');
const readableStream = fs.createReadStream('./abc.txt');
const writeableStream = fs.createWriteStream('./dest.txt');
let data = '';

readableStream.on('data', function(chunk) {
  writeableStream.write(chunk);
});

运行此程序时,可写流将创建一个新文件,因为它可以访问文件系统模块。可写流使用.write()方法输出目标上的数据。在上面的示例中,我们创建了一个名为dest.txt的新文件,该文件将包含与abc.txt相同的数据。

管道

管道是一种机制,通过它您可以从源读取数据并将其写入目标,而无需像上面那样编写如此多的代码,也不会使用.on.write方法。

如果我们要使用管道编写上面的示例,我们将写如下:

const fs = require('fs');
const readableStream = fs.createReadStream('./abc.txt');
const writeableStream = fs.createWriteStream('./dest.txt');

readableStream.pipe(writeableStream);

请注意我们删除了多少行代码。此外,我们现在只需要源文件路径和目标文件路径,以及读取和写入我们正在使用的数据.pipe()方法。

更多Node.js教程

学习更多Node.JS教程