本文概述
如今, 你可以在项目中使用许多开源库。它们非常好用, 可以让你做很多事情, 以了解事件是何时发生的。它们通常允许你向内部触发的事件添加回调。你是否曾经想过, 构建这样的允许使用回调的库的正确和最简单的方法是什么?好吧, 这是否是正确的方法并不是绝对的真理, 而是许多库依赖于Node.js的Event Emitter类。
所有发出事件的对象都是EventEmitter类的实例, 这些对象公开一个eventEmitter.on()函数, 该函数允许将一个或多个函数附加到该对象发出的命名事件。通常, 事件名称是驼峰式的字符串, 但是可以使用任何有效的JavaScript属性键。
在本文中, 你将学习如何将它们用于ECMAScript 5和6。
ES5
如果你使用的是ECMAScript 5, 那么对于Java初学者来说, 事件发射器的用法就不太清楚了:
// yourLibrary.js
// Instantiate event emitter and inherits
var EventEmitter = require('events');
var inherits = require('util').inherits;
// Create the constructor of YourLibrary and add the EventEmitter to the this context
function YourLibrary() {
EventEmitter.call(this);
}
// Use Inheritance to add the properties of the DownloadManager to event emitter
inherits(YourLibrary, EventEmitter);
// Export YourLibrary !
module.exports = YourLibrary;
在从YourLibrary创建实例期间执行EventEmitter.call(this)时, 会将EventEmitter构造函数声明的属性附加到YourLibrary。然后, 继承函数将原型方法从一个构造函数继承到另一个构造函数(你的构造函数YourLibrary和超级构造函数EventEmitter), 这样, 你的构造函数的原型将设置为从superConstructor创建的新对象。
由于你的库显然不会提供EventEmitter的相同方法, 因此你需要通过在module.exports行之前或之后使用原型将自己的函数添加到YourLibrary中:
//yourLibrary.js
YourLibrary.prototype.testAsyncMethod = function testAsyncMethod(someData) {
_this = this;
// Execute the data event in 2 seconds
setTimeout(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 file
var YourLibrary = require("./yourLibrary");
// Create an instance of YourLibrary
var libInstance = new YourLibrary();
// Add the "data" event listener to your library and add some callback
libInstance.on("data", function(data){
// Outputs: "Hello World, data test"
console.log(data);
});
// Execute the testAsyncMethod of your library
libInstance.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.js
var EventEmitter = require('events');
var inherits = require('util').inherits;
// Create the constructor of DownloadManager and add the EventEmitter to the this context
function DownloadManager() {
EventEmitter.call(this);
}
// Use Inheritance to add the properties of the event emitter to DownloadManager
inherits(DownloadManager, EventEmitter);
// Export the Download Manager
module.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 second
var 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 interval
setTimeout(function() {
var optionalDataResponse = {
filename: "imaginary.txt", filesize: 123123, fileUrl: URL
};
// Stop triggering progress
clearInterval(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 library
var DownloadManager = require("./fileDownloader");
// Create an instance of the Download Manager
var downloader = new DownloadManager();
// Add event listener of the progress of download
downloader.on("progress", function (progress){
console.log('Download progress: '+ progress +'%');
});
// Do something when the download of the file ends
downloader.on("downloadSuccess", function (response){
//{
// filename: "imaginary.txt", // filesize: 123123
//}
console.log(response);
});
// Start download
downloader.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 second
let 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 interval
setTimeout(() => {
let optionalDataResponse = {
filename: "imaginary.txt", filesize: 123123, fileUrl: URL
};
// Stop triggering progress
clearInterval(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 library
const DownloadManager = require('./fileDownloader');
// Create an instance of the Download Manager
const downloader = new DownloadManager();
// Add event listener of the progress of download
downloader.on("progress", (progress) => {
console.log(`Download progress ${progress}%`);
});
// Do something when the download of the file ends
downloader.on("downloadSuccess", (response) => {
//{
// filename: "imaginary.txt", // filesize: 123123
//}
console.log(response);
});
// Start download
downloader.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的EventEmitter类时, 你可以使用库中此类的所有方法, 因此不要忘记查看Node.js的文档以了解有关事件发射器的更多信息。
编码愉快!
评论前必须登录!
注册