事件是具有软件或硬件意义的动作。 它们是由于用户活动(例如鼠标单击或击键)或直接来自系统(例如错误或通知)而发出的。
JavaScript 语言使我们能够通过在事件处理程序中运行代码来响应事件。 由于 Node.js 基于 JavaScript,因此它在其事件驱动架构中利用了此功能。
在本文中,我们将讨论什么是事件发射器以及为什么要使用它们,以及如何构建自定义事件发射器。 让我们开始吧!
系统事件与自定义事件
1.系统事件
2.自定义事件
什么是事件发射器?
为什么使用事件发射器?
内的班级成员 EventEmitter
自定义事件发射器的一般模式
在 Node.js 中构建自定义事件发射器
创建事件发射器函数构造函数
向事件发射器原型添加方法
添加更多事件方法
这 addListener方法
这 listenersCount方法
中的事件 Node.js 是我们可以响应的应用程序中发生的操作。 Node.js 中有两种不同类型的事件,我们将在本文中介绍这两种类型:
系统事件发生在 Node.js 核心的 C++ 端,并由 Node.js 中使用的名为 libuv 的 C 库处理。
libuv 库处理计算机系统内部发生的低级事件,本质上是来自操作系统的低级事件,例如从互联网接收数据、完成读取文件等。
因为 Node.js 程序的流程是由事件(事件驱动编程)决定的,所以所有的 I/O 请求最终都会产生一个完成/失败事件。
旁注:JavaScript 没有处理系统事件的内置功能。
自定义事件发生在 Node.js 的 JavaScript 核心内。 这些事件由名为的 Node.js 类处理 EventEmitter.
EventEmitter是 Node.js 中的一个 JavaScript 类,用于处理自定义事件,因为 JavaScript 没有任何内置功能来处理事件。
通常,当 libuv 中发生事件时,libuv 会生成自定义 JavaScript 事件,以便轻松处理它们。 因此,即使 Node.js 事件实际上是两种不同类型的事件,它们也可能被视为单个实体。
在本文中,我们的重点将放在自定义 JavaScript 事件和事件发射器上。
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →
Node.js 使用如上所述的事件驱动编程模式。 这种设计模式用于生产和消费事件,主要用于前端(浏览器)。 诸如按钮单击或按键之类的用户操作会生成一个事件。
这些事件具有关联的函数,称为侦听器,当它们发出时,它们被调用以处理它们的相应事件。
事件驱动模式有两个主要组成部分:
事件处理程序(侦听器)。 这是发出事件时将调用的函数。 可以有多个侦听器在侦听一个事件。 当事件发出时,这些监听器将被同步调用。
侦听事件并调用相关侦听器来处理该事件的主循环。
Node.js 通过 EventEmitter班级。 这 EventEmitter类是 events核心 Node.js 模块。 它为我们提供了一种发出和处理事件的方式,并且它暴露了——除此之外—— on和 emit方法。
在本文中,我们将使用支持 Node.js 事件发射器的核心原理构建自定义事件发射器,以便我们更好地了解它们的工作原理。
下面的代码展示了如何使用 EventEmitter目的。
const EventEmitter = require('events'); const eventEmitter = new EventEmitter (); eventEmitter.on('greet', () => { console.log('Hello world!'); }); eventEmitter.emit('greet');
在上面的代码中, eventEmitter.on方法用于注册处理事件的侦听器,而 eventEmitter.emit方法用于发出事件。
通过发出 greet上面的事件,监听的函数 greet事件被调用,并且 Hello world!被记录到控制台。
公开的其他方法 EventEmitter对象是:
eventEmitter.once(): 这增加了一个一次性监听器
eventEmitter.off(): 这是一个别名 eventEmitter.removeListener(). 它从事件中删除事件侦听器
eventEmitter.listnersCount():这将返回侦听事件的侦听器数量
在 Node.js 中,还有其他可以发出事件的对象。 但是,所有发出事件的对象都是 EventEmitter班级。 为了更好地理解事件发射器,让我们自己构建。
不要错过 The Replay 来自 LogRocket 的精选时事通讯
了解 LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题
使用 React 的 useEffect 优化应用程序的性能
之间切换 在多个 Node 版本
了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画
探索 Tauri ,一个用于构建二进制文件的新框架
比较 NestJS 与 Express.js
Node.js 通过 EventEmitter班级。 这 EventEmitter类是 events核心 Node.js 模块。
事件发射器和监听器对于 Node.js 开发至关重要。 它们使 Node.js 运行时能够完成其单线程、异步、非阻塞 I/O 功能,这是 Node.js 出色性能的关键。
Node.js EventEmitter类为我们提供了一种发出和处理事件的方法。 在其中,它公开了许多 类成员 :
这 emit方法顺序和同步地调用每个监听触发事件的监听器——将提供的参数传递给每个监听器
这 on方法有两个参数:事件名称和事件监听器。 它将此事件侦听器函数添加到侦听器数组的末尾
off是的别名 emitter.removeListener()
这 removeListener方法有两个参数:事件名称和事件监听器。 然后在指定事件触发时从事件数组中删除此事件侦听器
addListener是的别名 on方法
事件发射器的一般思想是拥有一个事件对象,其键充当自定义事件,相应的值将是在事件发出时同步调用的侦听器数组。
考虑下面的代码:
const GreetHandlers = [ ()=> {console.log("Hello world!"), ()=> {console.log("Hello Developer!"), ()=> {console.log("Hello From LogRocket!"} ]; const events = { "greet": GreetHandlers }
在这里, events对象有一个属性: "greet". 此对象的每个属性名称都被视为一个唯一事件,这意味着 "greet"财产是一个事件。
的价值 "greet"属性(事件)是 GreetHandlers大批。 这是一个包含函数(或侦听器)的数组,当 "greet"事件发生。
为了同步调用这些函数,我们遍历数组并调用每个函数,如下所示:
GreetHandlers.forEach(listener => { listener(); }); // Output: // "Hello world!" // "Hello Developer!" // "Hello From LogRocket!"
上面的示例简要概述了 Node.js 事件发射器中使用的模式。 我们将使用与在下一节中构建自己的事件发射器相同的模式。
虽然 Node.js EventEmitter是一个 JavaScript 类,我们将使用函数构造函数构建我们自己的类,以便我们了解后台发生的事情。
JavaScript 中的类为我们提供了一种新的、简单的语法来处理 JavaScript 的 原型继承 。 因为 JavaScript 中的类是原型模式的语法糖,所以很多事情都发生在我们隐藏的引擎盖下。
要构建我们的自定义事件发射器,请按照以下步骤操作:
这应该有一个属性(事件对象)。
function Emitter( ) { this.events = { }; }
这 events上面的对象将作为保存所有自定义事件的主要对象。 每个 key和 value这个对象对应于一个事件和它的监听器数组。
function Emitter( ) { this.events = { "greet": [ ()=> {}, ()=> {}, ()=> {}, ], "speak": [ ()=> {}, ()=> {}, ()=> {}, ] }
面向对象的 JavaScript 为我们提供了一种在应用程序中共享属性和方法的简洁方式。 这是因为原型模式允许对象访问原型链下游的属性,这意味着对象可以访问其原型、其原型的原型以及其他地方的属性。
如果对象中不存在该方法或属性,则 JavaScript 引擎首先在对象的原型中搜索该方法或属性。 如果它在该对象的原型中没有找到它,它会继续沿着原型链向下搜索。 这种继承模式被称为原型继承。
由于 JavaScript 的原型继承,当我们向对象的原型添加属性和方法时,该对象的所有实例都可以访问它们。
请参见下面的代码中的此处,我们在其中添加 on方法:
Emitter.prototype.on = function (type, listener) { // check if the listener is a function and throw error if it is not if(typeof listener !== "function") { throw new Error("Listener must be a function!") } // create the event listener property (array) if it does not exist. this.events[type] = this.events[type] || []; // adds listners to the events array. this.events[type].push(listener); }
这增加了 on函数原型 Emitter对象,它允许所有实例 Emitter对象继承此方法。 这 on方法有两个参数,即 type和 listener(一个函数)。
首先, on方法检查是否 listener是一个函数。 如果不是,则会引发错误,如下所示:
if(typeof listener !== "function") { throw new Error("Listener must be a function!") }
此外,该 on方法检查是否 type的事件存在于 events对象(作为键)。 如果它不存在,它会添加一个事件(作为键)到 events对象并将其值设置为空数组。 最后,它将侦听器添加到相应的事件数组中: this.events[type].push(listener);.
现在让我们添加 emit方法:
Emitter.prototype.emit = function(type) { if (this.events[type]) { // checks if event is a property on Emitter this.events[type].forEach(function(listener) { // loop through that events array and invoke all the listeners inside it. listener( ); }) } } // if used as a node module. Exports this function constructor modules.exports = Emitter; // This makes it available from the require() // so we can make as many instances of it as we want.
上面的代码添加了 emit的方法 Emitters原型。 它只是检查事件类型是否存在(作为键) events目的。 如果它存在,那么它会调用上面已经讨论过的所有相应的侦听器。
this.events[type]返回对应事件属性的值,该属性是一个包含监听器的数组。
因此,下面的代码循环遍历数组并同步调用其所有侦听器。
this.events[type].forEach(function(listener) { // loop through that events array and invoke all the listeners inside it. listener( ); })
要使用我们的事件发射器,我们必须手动发射一个事件。
// if used in a Node.js environment require the module as seen below. const Emitter = require('./emitter'); const eventEmitter = new Emitter(); eventEmitter.on('greet', ()=> { console.log("Hello World!"); }); eventEmitter.on('greet', ()=> { console.log("Hello from LogRocket!"); }); eventEmitter.emit("greet"); // manually emit an event
在上面的代码中,我们首先需要并创建一个实例 Emitter使用这个的模块:
const Emitter = require('./emitter'); const eventEmitter = new Emitter();
然后我们将监听器分配给 "greet"事件使用 on方法。
eventEmitter.on('greet', ()=> { console.log("Hello World!"); }); eventEmitter.on('greet', ()=> { console.log("Hello from LogRocket!"); });
最后,我们手动发出 greet使用这一行的事件:
emtr.emit("greet");
值得注意的是, on方法实际上是 addListener事件发射器中的方法。 因此,我们需要重构实现。
Emitter.prototype.addListener = function (type, listener) { // check if the listener is a function and throw error if it is not if (typeof listener !== "function") { throw new Error("Listener must be a function!"); } // create the event listener property (array) if it does not exist. this.events[type] = this.events[type] || []; // adds listners to the events array. this.events[type].push(listener); }; Emitter.prototype.on = function (type, listener) { return this.addListener(type, listener); };
上面的代码仍然有效,但在这个版本中, addListener和 on方法做同样的事情。
还有 listenersCount方法。 这将返回正在侦听特定事件的函数(侦听器)的总数。 我们将在下面实现:
Emitter.prototype.listenerCount = function (type) { let listnersCount = 0; let listeners = this.events[type] || []; listnersCount = listners.length; console.log("listeners listnersCount", listnersCount); return listnersCount; };
在这里,我们告诉简单存储指定事件的侦听器数组到 listeners变量使用:
let listeners = this.events[type] || [];
如果未找到该事件,则存储一个空数组。 然后,我们返回 length的 listener variable.
Node.js 中的事件发射器遵循同样的想法,但它们有很多额外的功能。 我们只是构建了一个简单的版本。 您可以 在此处使用纯 JavaScript 编写最终代码 。
事件发射器是 Node.js JavaScript 核心许多部分的基本构建块。 所有发出事件的 Node.js 对象,例如流和 HTTP 模块,都是 EventEmitter班级。 它是 Node.js 中的一个重要对象,由 events模块。
通过 EventEmitter类,Node.js 将事件驱动编程带到了服务器端。 我希望通过构建我们设计的事件发射器,您能够了解更多关于 Node.js EventEmitter班级。
部署基于节点的 Web 应用程序或网站是很容易的部分。 确保您的 Node 实例继续为您的应用程序提供资源是事情变得更加困难的地方。 如果您有兴趣确保对后端或第三方服务的请求成功, 请尝试 LogRocket 。
LogRocket 就像一个用于网络和移动应用程序的 DVR,记录用户与您的应用程序交互时发生的所有事情。 无需猜测问题发生的原因,您可以汇总和报告有问题的网络请求,以快速了解根本原因。
LogRocket 检测您的应用程序以记录基准性能时间,例如页面加载时间、第一个字节的时间、缓慢的网络请求,并记录 Redux、NgRx 和 Vuex 操作/状态。