如何轻松在Node.js中将事件发射器与ES5和ES6结合使用

本文概述

  • 使用ECMAScript 5的示例
  • 使用ECMAScript 6的示例
如今, 你可以在项目中使用许多开源库。它们非常好用, 可以让你做很多事情, 以了解事件是何时发生的。它们通常允许你向内部触发的事件添加回调。你是否曾经想过, 构建这样的允许使用回调的库的正确和最简单的方法是什么?好吧, 这是否是正确的方法并不是绝对的真理, 而是许多库依赖于Node.js的Event Emitter类。
所有发出事件的对象都是EventEmitter类的实例, 这些对象公开一个eventEmitter.on()函数, 该函数允许将一个或多个函数附加到该对象发出的命名事件。通常, 事件名称是驼峰式的字符串, 但是可以使用任何有效的JavaScript属性键。
在本文中, 你将学习如何将它们用于ECMAScript 5和6。
ES5
如果你使用的是ECMAScript 5, 那么对于Java初学者来说, 事件发射器的用法就不太清楚了:
// yourLibrary.js// Instantiate event emitter and inheritsvar EventEmitter = require('events'); var inherits = require('util').inherits; // Create the constructor of YourLibrary and add the EventEmitter to the this contextfunction YourLibrary() {EventEmitter.call(this); }// Use Inheritance to add the properties of the DownloadManager to event emitterinherits(YourLibrary, EventEmitter); // Export YourLibrary !module.exports = YourLibrary;

在从YourLibrary创建实例期间执行EventEmitter.call(this)时, 会将EventEmitter构造函数声明的属性附加到YourLibrary。然后, 继承函数将原型方法从一个构造函数继承到另一个构造函数(你的构造函数YourLibrary和超级构造函数EventEmitter), 这样, 你的构造函数的原型将设置为从superConstructor创建的新对象。
由于你的库显然不会提供EventEmitter的相同方法, 因此你需要通过在module.exports行之前或之后使用原型将自己的函数添加到YourLibrary中:
//yourLibrary.jsYourLibrary.prototype.testAsyncMethod = function testAsyncMethod(someData) {_this = this; // Execute the data event in 2 secondssetTimeout(function(){// Emit the data event that sends the same data providen by testAsyncMethod _this.emit("data", someData); }, 2000); };

上一个函数将testAsyncMethod添加到你的库中, 该方法将一些数据作为第一个参数, 并将通过data事件再次发送, 该事件是使用EventEmitter类的继承方法发出的。通过这种方式, YourLibrary使用Node.js的事件管理器, 并且可以用来创建有条理且易于阅读的代码:
// otherfile.js// Require YourLibrary filevar YourLibrary = require("./yourLibrary"); // Create an instance of YourLibraryvar libInstance = new YourLibrary(); // Add the "data" event listener to your library and add some callbacklibInstance.on("data", function(data){// Outputs: "Hello World, data test"console.log(data); }); // Execute the testAsyncMethod of your librarylibInstance.testAsyncMethod("Hello World, data test");

尽管处理异步事件可能会有些棘手, 但最后一切都会变得有意义, 因此, 如果你还不了解它, 请耐心等待并仔细分析示例。
ES6
使用EcmaScript 6, 该任务确实得到了简化, 并且比ES5更容易处理和理解。但是, 它的工作方式相同:通过使用extends关键字, 你的类可以扩展EventEmitter, 从而明显继承其方法, 因此可以使用emit方法触发事件:
const EventEmitter = require('events'); class YourLibrary extends EventEmitter {constructor() {super(); }testAsyncMethod(data) {this.emit('data', data); }}module.exports = YourLibrary

然后, 可以从另一个文件轻松使用YourLibrary:
const MyLibrary = require('./yourLibrary'); const libInstance = new MyLibrary(); libInstance.on('data', (data) => {// Outputs : Received data: "Hello World, data test"console.log(`Received data: "${data}"`); }); libInstance.testAsyncMethod("Hello World, data test");

如你所见, 这比使用ES6处理相同的代码要容易得多。
使用ECMAScript 5的示例如果你没有在介绍中找到它, 请放心, 我们确信将示例作为现实世界中的库(或类似的东西)来处理将有助于你理解。在此示例中, 假设我们将使用下载管理器库, 该库确实非常简单, 它提供了一种从服务器异步下载文件的方法。使用Node.js的Event Emitters类, 你可以知道文件的下载时间, 并可以了解下载的进度。
该示例并没有真正下载任何内容, 我们只是使用setInterval(用于progress事件)和setTimeout(用于downloadSuccess事件)函数来模拟下载。在工作区的某个文件夹中创建fileDownloader.js, 并在其上添加以下代码:
// fileDownloader.jsvar EventEmitter = require('events'); var inherits = require('util').inherits; // Create the constructor of DownloadManager and add the EventEmitter to the this contextfunction DownloadManager() {EventEmitter.call(this); }// Use Inheritance to add the properties of the event emitter to DownloadManagerinherits(DownloadManager, EventEmitter); // Export the Download Managermodule.exports = DownloadManager; //// Write your library down here by prototyping !///** * @param URL {String} Url of the imaginary file to download */DownloadManager.prototype.downloadAsync = function downloadAsync(URL) {var _this = this; var progress = 0; console.log('Download file "' + URL + '" ...'); // Trigger the progress event every secondvar progressInterval = setInterval(function() {progress += 20; // Emit progress event with the progress as argument_this.emit('progress' , progress); }, 1000); // Trigger the downloadSuccess event in 5.5 seconds and clear the progress intervalsetTimeout(function() {var optionalDataResponse = {filename: "imaginary.txt", filesize: 123123, fileUrl: URL}; // Stop triggering progressclearInterval(progressInterval); // Use the emit method of the EventEmitter to trigger the downloadSuccess event !_this.emit('downloadSuccess' , optionalDataResponse); }, 5500); };

然后可以按以下方式使用下载管理器(在本示例中为index.js文件):
// index.js// Require the download manager libraryvar DownloadManager = require("./fileDownloader"); // Create an instance of the Download Managervar downloader = new DownloadManager(); // Add event listener of the progress of downloaddownloader.on("progress", function (progress){console.log('Download progress: '+ progress +'%'); }); // Do something when the download of the file endsdownloader.on("downloadSuccess", function (response){//{//filename: "imaginary.txt", //filesize: 123123//}console.log(response); }); // Start downloaddownloader.downloadAsync("file.txt");

并使用节点index.js执行脚本, 你将获得以下输出:
Download file "file.txt" ...Download progress: 20%Download progress: 40%Download progress: 60%Download progress: 80%Download progress: 100%{ filename: 'imaginary.txt', filesize: 123123, fileUrl: 'file.txt' }

使用ECMAScript 6的示例对于ES6中的示例, 我们将使用与ES5中第一个示例相同的想法, 假设我们将使用Download Manager库, 这个库非常简单, 它提供了一种从服务器异步下载文件的方法。使用Node.js的Event Emitters类, 你可以知道文件的下载时间, 并可以了解下载的进度。
该示例并没有真正下载任何内容, 我们只是使用setInterval(用于progress事件)和setTimeout(用于downloadSuccess事件)函数来模拟下载。在工作区的某个文件夹中创建fileDownloader.js, 并在其上添加以下代码:
const EventEmitter = require('events'); class DownloadManager extends EventEmitter {constructor() {super(); }downloadAsync(URL) {let _this = this; let progress = 0; console.log(`Download file '${URL}' ...`); // Trigger the progress event every secondlet progressInterval = setInterval(() => {progress += 20; // Emit progress event with the progress as argument_this.emit('progress', progress); }, 1000); // Trigger the downloadSuccess event in 5.5 seconds and clear the progress intervalsetTimeout(() => {let optionalDataResponse = {filename: "imaginary.txt", filesize: 123123, fileUrl: URL}; // Stop triggering progressclearInterval(progressInterval); // Use the emit method of the EventEmitter to trigger the downloadSuccess event !_this.emit('downloadSuccess', optionalDataResponse); }, 5500); }}module.exports = DownloadManager

下载管理器可用于其他文件(index.js):
// index.js// Require the download manager libraryconst DownloadManager = require('./fileDownloader'); // Create an instance of the Download Managerconst downloader = new DownloadManager(); // Add event listener of the progress of downloaddownloader.on("progress", (progress) => {console.log(`Download progress ${progress}%`); }); // Do something when the download of the file endsdownloader.on("downloadSuccess", (response) => {//{//filename: "imaginary.txt", //filesize: 123123//}console.log(response); }); // Start downloaddownloader.downloadAsync("file.txt");

使用节点index.js执行脚本, 你将获得以下输出(与ES5中的示例相同):
Download file "file.txt" ...Download progress: 20%Download progress: 40%Download progress: 60%Download progress: 80%Download progress: 100%{ filename: 'imaginary.txt', filesize: 123123, fileUrl: 'file.txt' }

【如何轻松在Node.js中将事件发射器与ES5和ES6结合使用】在使用Node.js的EventEmitter类时, 你可以使用库中此类的所有方法, 因此不要忘记查看Node.js的文档以了解有关事件发射器的更多信息。
编码愉快!

    推荐阅读