当前位置: 首页 > 工具软件 > speak.js > 使用案例 >

如何构建自定义 Node.js 事件发射器

长孙德惠
2023-12-01

事件是具有软件或硬件意义的动作。 它们是由于用户活动(例如鼠标单击或击键)或直接来自系统(例如错误或通知)而发出的。

JavaScript 语言使我们能够通过在事件处理程序中运行代码来响应事件。 由于 Node.js 基于 JavaScript,因此它在其事件驱动架构中利用了此功能。

在本文中,我们将讨论什么是事件发射器以及为什么要使用它们,以及如何构建自定义事件发射器。 让我们开始吧!

  • 系统事件与自定义事件

    • 1.系统事件

    • 2.自定义事件

  • 什么是事件发射器?

  • 为什么使用事件发射器?

  • 内的班级成员 EventEmitter

  • 自定义事件发射器的一般模式

  • 在 Node.js 中构建自定义事件发射器

    • 创建事件发射器函数构造函数

    • 向事件发射器原型添加方法

  • 添加更多事件方法

    • 这 addListener方法

    • 这 listenersCount方法

系统事件与自定义事件

中的事件 Node.js 是我们可以响应的应用程序中发生的操作。 Node.js 中有两种不同类型的事件,我们将在本文中介绍这两种类型:

1.系统事件

系统事件发生在 Node.js 核心的 C++ 端,并由 Node.js 中使用的名为 libuv 的 C 库处理。

libuv 库处理计算机系统内部发生的低级事件,本质上是来自操作系统的低级事件,例如从互联网接收数据、完成读取文件等。

因为 Node.js 程序的流程是由事件(事件驱动编程)决定的,所以所有的 I/O 请求最终都会产生一个完成/失败事件。

旁注:JavaScript 没有处理系统事件的内置功能。

2.自定义事件

自定义事件发生在 Node.js 的 JavaScript 核心内。 这些事件由名为的 Node.js 类处理 EventEmitter.

EventEmitter是 Node.js 中的一个 JavaScript 类,用于处理自定义事件,因为 JavaScript 没有任何内置功能来处理事件。

通常,当 libuv 中发生事件时,libuv 会生成自定义 JavaScript 事件,以便轻松处理它们。 因此,即使 Node.js 事件实际上是两种不同类型的事件,它们也可能被视为单个实体。

在本文中,我们的重点将放在自定义 JavaScript 事件和事件发射器上。


超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →


什么是事件发射器?

Node.js 使用如上所述的事件驱动编程模式。 这种设计模式用于生产和消费事件,主要用于前端(浏览器)。 诸如按钮单击或按键之类的用户操作会生成一个事件。

这些事件具有关联的函数,称为侦听器,当它们发出时,它们被调用以处理它们的相应事件。

事件驱动模式有两个主要组成部分:

  1. 事件处理程序(侦听器)。 这是发出事件时将调用的函数。 可以有多个侦听器在侦听一个事件。 当事件发出时,这些监听器将被同步调用。

  2. 侦听事件并调用相关侦听器来处理该事件的主循环。

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对象是:

  1. eventEmitter.once(): 这增加了一个一次性监听器

  2. eventEmitter.off(): 这是一个别名 eventEmitter.removeListener(). 它从事件中删除事件侦听器

  3. eventEmitter.listnersCount():这将返回侦听事件的侦听器数量

在 Node.js 中,还有其他可以发出事件的对象。 但是,所有发出事件的对象都是 EventEmitter班级。 为了更好地理解事件发射器,让我们自己构建。


来自 LogRocket 的更多精彩文章:

  • 不要错过 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 出色性能的关键。

内的班级成员 EventEmitter

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 中构建自定义事件发射器

虽然 Node.js EventEmitter是一个 JavaScript 类,我们将使用函数构造函数构建我们自己的类,以便我们了解后台发生的事情。

JavaScript 中的类为我们提供了一种新的、简单的语法来处理 JavaScript 的 原型继承 。 因为 JavaScript 中的类是原型模式的语法糖,所以很多事情都发生在我们隐藏的引擎盖下。

要构建我们的自定义事件发射器,请按照以下步骤操作:

1.创建事件发射器函数构造函数

这应该有一个属性(事件对象)。

function Emitter( ) {
    this.events = { };
}

这 events上面的对象将作为保存所有自定义事件的主要对象。 每个 key和 value这个对象对应于一个事件和它的监听器数组。

function Emitter( ) {
    this.events = {
      "greet": [
                  ()=> {},
                  ()=> {},
                  ()=> {}, 
              ],
      "speak": [
                  ()=> {},
                  ()=> {},
                  ()=> {}, 
              ]
}

2.给事件发射器原型添加方法

面向对象的 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");

Google翻译恢复访问软件,修复谷歌翻译服务,懒人一键操作即可搞定!

上图显示了运行我们的代码的结果。 在此处查看沙盒中的完整代码 。

添加更多事件方法

这 addListener方法

值得注意的是, 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方法

还有 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班级。

仅200个 监控生产中失败和缓慢的网络请求

部署基于节点的 Web 应用程序或网站是很容易的部分。 确保您的 Node 实例继续为您的应用程序提供资源是事情变得更加困难的地方。 如果您有兴趣确保对后端或第三方服务的请求成功, 请尝试 LogRocket 。

LogRocket 就像一个用于网络和移动应用程序的 DVR,记录用户与您的应用程序交互时发生的所有事情。 无需猜测问题发生的原因,您可以汇总和报告有问题的网络请求,以快速了解根本原因。

LogRocket 检测您的应用程序以记录基准性能时间,例如页面加载时间、第一个字节的时间、缓慢的网络请求,并记录 Redux、NgRx 和 Vuex 操作/状态。

 类似资料: