在 Node.js 中使用事件发射器

作者选择了 COVID-19 救援基金作为 Write for Donations计划的一部分接受捐款。

介绍

Event emittersNode.js中的对象,通过发送信息来触发事件,以便发出信号,即已完成一项操作。 JavaScript开发人员可以从事件发射器中编写代码,以便在触发这些事件时执行 函数

通常在Node.js中,当我们希望在完成另一个操作时发生某种动作时,我们会使用(https://andsky.com/tech/tutorials/how-to-write-asynchronous-code-in-node-js)技术,例如嵌套回调或链承诺。然而,这些技术紧密地结合了触发动作和结果动作,使其难以在未来更改结果动作。事件发行商提供一种不同的方式来构建这种关系: publish-subscribe 模式。在这个软件架构模式中,一个发行商(事件发行商)发送消息(事件发行者)并接收事件并执行行动,该模式的力量在于发行商不需要知道订阅者。一个发行商发行消息,而订阅者必须以各自的方式回应它。如果我们想要改变我们的行为,我们的应用程序可以如何调整订阅者对事件的回应而不需要

在本文中,我们将创建一个TicketManager(JavaScript类)的事件倾听器(https://andsky.com/tech/tutorials/understanding-classes-in-javascript),允许用户购买门票。我们将为购买事件设置倾听器,每次购买门票都会触发。

前提条件

  • Node.js 安装在您的开发机器上。本教程使用 10.20.1 版本。 要在 macOS 或 Ubuntu 18.04 上安装此功能,请遵循 如何在 macOS 上安装 Node.js 并创建本地开发环境或 [如何在 Ubuntu 18.04 上安装 Node.js]的 安装使用 PPA 部分的步骤。
  • 本文中的主要示例使用了 JavaScript 类,因为它们在 ES2015 被引入(通常称为 ES6))。

步骤 1 - 发布事件

在此步骤中,我们将探索在 Node.js 中创建事件发射器的两种最常见方法,第一种是直接使用事件发射器对象,第二种是创建一个扩展事件发射器对象的对象。

决定使用哪个取决于您的事件与您的对象的行动是如何关联的。如果您要发送的事件是对象的行动的效果,那么您可能会从事件发射对象扩展到其功能,以便您可以轻松访问它。

让我们开始创建一个独立的事件发射对象,我们将开始创建一个文件夹来存储我们所有的代码,在您的终端中创建一个名为事件发射器的新文件夹:

1mkdir event-emitters

然后输入这个文件夹:

1cd event-emitters

在文本编辑器中打开第一个事件发射器,‘firstEventEmitter.js’,我们将使用‘nano’,因为它在终端中可用:

1nano firstEventEmitter.js

在 Node.js 中,我们通过EventEmitter类播放事件. 这个类是events模块的一部分. 让我们先在我们的文件中加载events模块,然后添加下面的行:

1[label event-emitters/firstEventEmitter.js]
2const { EventEmitter } = require("events");

随着类被导入,我们可以使用它来创建一个新的对象实例:

1[label event-emitters/firstEventEmitter.js]
2const { EventEmitter } = require("events");
3
4const firstEmitter = new EventEmitter();

让我们通过在firstEventEmitter.js的末尾添加以下突出的行来发出一个事件:

1[label event-emitters/firstEventEmitter.js]
2const { EventEmitter } = require("events");
3
4const firstEmitter = new EventEmitter();
5
6firstEmitter.emit("My first event");

我们需要将事件的名称传递给它作为一个字符串,我们可以添加事件名称之后的任何数目,只有一个名称的事件是相当有限的,其他参数允许我们向听众发送数据。当我们设置我们的票券管理器时,我们的事件会在发生时传递有关购买的数据。记住事件的名称,因为事件听众将用这个名称识别它。

<$>[注] **注:**虽然我们在本示例中没有捕捉到它,但如果事件没有听众,则emit()函数返回true

让我们运行这个文件,看看会发生什么。保存和退出nano,然后用node命令执行该文件:

1node firstEventEmitter.js

当脚本完成执行时,您将不会在终端中看到任何输出. 这是因为我们不会在firstEventEmitter.js中记录任何消息,并且没有任何听取被发送的事件的信息。

我们将通过创建一个门票管理示例应用程序来完成发布、听取和对事件采取行动的例子。门票管理器将暴露购买门票的功能。当购买门票时,将发送一项与购买者的细节相关的事件。

它将扩展EventEmitter类,以便我们不必创建一个单独的事件发射对象来发射事件。

在相同的工作目录中,创建并打开一个名为 `ticketManager.js’的新文件:

1nano ticketManager.js

与第一个事件发行器一样,我们需要从事件模块中导入EventEmitter类。

1[label event-emitters/ticketManager.js]
2const EventEmitter = require("events");

现在,创建一个新的TicketManager类,这将很快定义购买门票的方法:

1[label event-emitters/ticketManager.js]
2const EventEmitter = require("events");
3
4class TicketManager extends EventEmitter {}

在这种情况下,‘TicketManager’类扩展了‘EventEmitter’类,这意味着‘TicketManager’类继承了‘EventEmitter’类的方法和属性。

在我们的门票管理器中,我们希望提供可购买的门票的初始供应。我们将通过在我们的 constructor()中接受初始供应,这是在创建一类的新对象时呼叫的特殊功能。

1[label event-emitters/ticketManager.js]
2const EventEmitter = require("events");
3
4class TicketManager extends EventEmitter {
5    constructor(supply) {
6        super();
7        this.supply = supply;
8    }
9}

虽然我们声明TicketManagerEventEmitter的子类,但我们仍然需要拨打super()才能访问EventEmitter的方法和属性。

最后,我们为对象创建一个供应属性,并给它由构建者传递的价值。

现在,让我们添加一个购买( )方法,在购买门票时将被调用。

添加buy()方法如下:

 1[label event-emitters/ticketManager.js]
 2const EventEmitter = require("events");
 3
 4class TicketManager extends EventEmitter {
 5    constructor(supply) {
 6        super();
 7        this.supply = supply;
 8    }
 9
10    buy(email, price) {
11        this.supply--;
12        this.emit("buy", email, price, Date.now());
13    }
14}

在「購買()」函數中,我們採取購買者的電子郵件地址和他們為票價付出的價格,然後將票價減少一分之一,結束時發出「購買」事件,這次我們發出附加資料的事件:在函數中傳送的電子郵件和價格,以及購買時刻印記。

为了使我们的其他 Node.js 模块(https://andsky.com/tech/tutorials/how-to-create-a-node-js-module)能够使用这个类,我们需要导出它。

1[label event-emitters/ticketManager.js]
2...
3
4module.exports = TicketManager

保存和退出文件。

我们已经完成了事件发行商TicketManager的设置,现在我们已经设置了东西来推动事件,我们可以继续阅读和处理这些事件。

步骤2 - 聆听事件

Node.js 允许我们使用事件发射对象的 on() 函数为事件添加一个收听器,这会收听特定事件名称并在事件启动时发出回调。

1eventEmitter.on(event_name, callback_function) {
2    action
3}

<$>[note] 注::Node.js将on()方法代替为addListener()。他们执行相同的任务. 在本教程中,我们将继续使用on() <$>

让我们从听我们的第一个事件中获得一些第一手的经验,创建一个名为firstListener.js的新文件:

1nano firstListener.js

为了展示on()函数的运作方式,让我们在接收我们的第一个事件时登录一个简单的消息。

首先,让我们将TicketManager导入到我们的新 Node.js 模块中,然后将以下代码添加到firstListener.js:

1[label event-emitters/firstListener.js]
2const TicketManager = require("./ticketManager");
3
4const ticketManager = new TicketManager(10);

请记住,TicketManager对象在创建时需要其初始的门票供应,这就是为什么我们通过了10论点。

现在让我们添加我们的第一个 Node.js 事件倾听器,它会听到购买事件,添加以下突出代码:

1[label event-emitters/firstListener.js]
2const TicketManager = require("./ticketManager");
3
4const ticketManager = new TicketManager(10);
5
6ticketManager.on("buy", () => {
7    console.log("Someone bought a ticket!");
8});

为了添加一个新的倾听器,我们使用了on()函数,该函数是ticketManager对象的一部分。on()方法可用于所有事件发送对象,并且由于TicketManager继承了EventEmitter类,所以此方法可用于所有TicketManager实例对象。

on()方法的第二个参数是回调函数,写作为 箭头函数。该函数中的代码在事件发行后运行。

现在我们已经设置了一个倾听器,让我们使用‘buy()’函数,以便该事件发射。

1[label event-emitters/firstListener.js]
2...
3
4ticketManager.buy("[email protected]", 20);

这将执行购买方法,使用用户电子邮件[email protected]20的票价。

保存和退出文件。

现在用node运行此脚本:

1node firstListener.js

您的控制台将显示这些:

1[secondary_label Output]
2Someone bought a ticket!

您的第一个活动倾听器工作了 让我们看看如果我们购买多个门票会发生什么。在文本编辑器中重新打开您的firstListener.js:

1nano firstListener.js

在文件的末尾,再次调用buy()函数:

1[label event-emitters/firstListener.js]
2...
3
4ticketManager.buy("[email protected]", 20);
5ticketManager.buy("[email protected]", 20);

让我们用 Node.js 运行脚本,看看会发生什么:

1node firstListener.js

您的控制台将显示这些:

1[secondary_label Output]
2Someone bought a ticket!
3Someone bought a ticket!

由于买()函数被调用了两次,所以发出了两个事件。

有时我们只对第一次发起事件的听力感兴趣,而不是它发出的所有次数。

就像「on()」一樣,「once()」函數接受事件名稱為其第一個論點,以及當事件啟動為其第二個論點時呼叫的召回函數。

让我们通过编辑firstListener.js来看到once()在行动中:

1nano firstListener.js

在文件末尾,使用once()添加一个新的事件倾听器,如下列突出的行:

 1[label event-emitters/firstListener.js]
 2const TicketManager = require("./ticketManager");
 3
 4const ticketManager = new TicketManager(10);
 5
 6ticketManager.on("buy", () => {
 7        console.log("Someone bought a ticket!");
 8});
 9
10ticketManager.buy("[email protected]", 20);
11ticketManager.buy("[email protected]", 20);
12
13ticketManager.once("buy", () => {
14    console.log("This is only called once");
15});

保存和退出文件,并使用节点运行此程序:

1node firstListener.js

结果与上次相同:

1[secondary_label Output]
2Someone bought a ticket!
3Someone bought a ticket!

虽然我们添加了once()的新事件倾听器,但在事件发射后,它被添加了。 因此,倾听者没有检测到这两个事件。

让我们添加一些更多的买( )函数呼叫,以便我们可以确认一次( )倾听器只响应一次。

1nano firstListener.js

在文件的末尾添加以下呼叫:

1[label event-emitters/firstListener.js]
2...
3
4ticketManager.once("buy", () => {
5    console.log("This is only called once");
6});
7
8ticketManager.buy("[email protected]", 20);
9ticketManager.buy("[email protected]", 20);

保存和退出,然后运行此程序:

1node firstListener.js

你的输出将是:

1[secondary_label Output]
2Someone bought a ticket!
3Someone bought a ticket!
4Someone bought a ticket!
5This is only called once
6Someone bought a ticket!

添加一个新的事件倾听器不会删除以前的,所以我们添加的第一个事件倾听器仍然活跃并记录消息。

由于On()的事件听众在Once()的事件听众之前被宣布,所以我们看到有人买了门票!这只叫一次之前。

最后,当最后一次调用买( )时,事件发射器只有第一个以on()创建的聆听器,如前所述,当以once()创建的事件聆听器接收一个事件时,它会自动删除。

现在我们已经添加了事件倾听器来检测我们的发射器,我们将看到如何与这些倾听者捕捉数据。

步骤 3 – 捕获事件数据

到目前为止,您已经设置了事件倾听器来响应发送的事件,发送的事件也会传输数据,让我们看看如何捕捉与事件相关的数据。

我们将首先创建一些新的 Node.js 模块:一个电子邮件服务和一个数据库服务. 它们将用于模拟发送电子邮件并保存到数据库。

让我们先编辑我们的电子邮件服务模块,在文本编辑器中打开文件:

1nano emailService.js

我们的电子邮件服务由一个类组成,其中包含一种方法,即发送( )。这个方法预计发送的电子邮件和购买事件。

1[label event-emitters/emailService.js]
2class EmailService {
3    send(email) {
4        console.log(`Sending email to ${email}`);
5    }
6}
7
8module.exports = EmailService

此代码会创建一个包含发送( )函数的EmailService类别,而不是发送实际的电子邮件,它会使用 template literals来登录向控制台的消息,该消息将包含购买机票的人的电子邮件地址。

打开 databaseService.js 与您的文本编辑器:

1nano databaseService.js

数据库服务通过其保存()方法将我们的购买数据存储到数据库中,将以下代码添加到databaseService.js:

1[label event-emitters/databaseService.js]
2class DatabaseService {
3    save(email, price, timestamp) {
4        console.log(`Running query: INSERT INTO orders VALUES (email, price, created) VALUES (${email}, ${price}, ${timestamp})`);
5    }
6}
7
8module.exports = DatabaseService

与电子邮件服务的发送()方法类似,保存()函数使用伴随着购买事件的数据,将其登录到控制台,而不是实际上将其插入数据库。

我们将使用我们的最后一个文件将TicketManager,EmailServiceDatabaseService合并在一起,它将为购买事件设置一个倾听器,并呼叫电子邮件服务的发送()函数和数据库服务的保存()函数。

在文本编辑器中打开index.js文件:

1nano index.js

首先要做的是导入我们正在使用的模块:

1[label event-emitters/index.js]
2const TicketManager = require("./ticketManager");
3const EmailService = require("./emailService");
4const DatabaseService = require("./databaseService");

接下来,让我们为我们导入的类创建对象,我们将为此演示设置一个低门票供应量为3个:

1[label event-emitters/index.js]
2const TicketManager = require("./ticketManager");
3const EmailService = require("./emailService");
4const DatabaseService = require("./databaseService");
5
6const ticketManager = new TicketManager(3);
7const emailService = new EmailService();
8const databaseService = new DatabaseService();

现在我们可以使用实例化对象设置我们的倾听器. 每当有人购买门票时,我们希望向他们发送电子邮件,并将数据保存到数据库中。

 1[label event-emitters/index.js]
 2const TicketManager = require("./ticketManager");
 3const EmailService = require("./emailService");
 4const DatabaseService = require("./databaseService");
 5
 6const ticketManager = new TicketManager(3);
 7const emailService = new EmailService();
 8const databaseService = new DatabaseService();
 9
10ticketManager.on("buy", (email, price, timestamp) => {
11    emailService.send(email);
12    databaseService.save(email, price, timestamp);
13});

与之前一样,我们使用on()方法添加一个听者。这次的区别是,我们在回调函数中有三个参数. 每个参数都与事件发出的数据相匹配。

1[label event-emitters/ticketManager.js]
2this.emit("buy", email, price, Date.now());

在我们的回复中,我们首先捕获发送方的电子邮件,然后捕获价格,最后捕获Date.now()数据,我们将其捕获为时刻印

当我们的听众检测到一个购买事件时,它会从emailService对象中调用send()函数以及databaseService中的save()函数。

1[label event-emitters/index.js]
2...
3
4ticketManager.buy("[email protected]", 10);

保存和退出编辑器 现在让我们用节点运行这个脚本,并观察接下来会发生什么。

1node index.js

您将看到以下结果:

1[secondary_label Output]
2Sending email to [email protected]
3Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588720081832)

数据在我们的回复函数中被成功捕获并返回. 有了这些知识,您可以为各种发射器设置不同的事件名称和数据的收听器。

接下来,让我们看看如何处理错误事件以及我们应该遵循哪些标准。

步骤4:处理错误事件

如果事件发射器无法执行其操作,它应该发出一个事件来信号该操作失败. 在 Node.js 中,事件发射器发出信号失败的标准方式是发出一个 _error 事件。

错误事件的名称必须设置为错误。它也必须伴随着一个错误对象。

我们的票券管理员每次调用买( )函数时都会减少一个,目前没有什么可以阻止它出售的票数超过可用的票数。 让我们修改买( )函数,以便如果票价达到0并且有人想要购买票数,我们会发出一个错误,表示我们已经没有库存。

在文本编辑器中再次打开ticketManager.js:

1nano ticketManager.js

现在编辑buy()函数如下:

 1[label event-emitters/ticketManager.js]
 2...
 3
 4buy(email, price) {
 5    if (this.supply > 0) {
 6        this.supply;
 7        this.emit("buy", email, price, Date.now());
 8        return;
 9    }
10
11    this.emit("error", new Error("There are no more tickets left to purchase"));
12}
13...

如果我们没有其他票,我们会发出一个错误事件。

保存和退出文件 让我们尝试将这个错误扔进我们的 index.js 文件. 目前,我们只购买了一张票. 我们用三张票实例了ticketManager对象,所以如果我们试图购买四张票,我们应该收到一个错误。

用文本编辑器编辑index.js:

1nano index.js

现在在文件末尾添加下列行,这样我们就可以购买四张票:

1[label event-emitters/index.js]
2...
3
4ticketManager.buy("[email protected]", 10);
5ticketManager.buy("[email protected]", 10);
6ticketManager.buy("[email protected]", 10);
7ticketManager.buy("[email protected]", 10);

保存和退出编辑器。

让我们执行这个文件,看看发生了什么:

1node index.js

你的输出将是:

 1[secondary_label Output]
 2Sending email to [email protected]
 3Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932796)
 4Sending email to [email protected]
 5Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932812)
 6Sending email to [email protected]
 7Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588724932812)
 8events.js:196
 9      throw er; // Unhandled 'error' event
10      ^
11
12Error: There are no more tickets left to purchase
13    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28)
14    at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15)
15    at Module._compile (internal/modules/cjs/loader.js:1128:30)
16    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)
17    at Module.load (internal/modules/cjs/loader.js:983:32)
18    at Function.Module._load (internal/modules/cjs/loader.js:891:14)
19    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
20    at internal/main/run_main_module.js:17:47
21Emitted 'error' event on TicketManager instance at:
22    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:14)
23    at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15)
24    [... lines matching original stack trace ...]
25    at internal/main/run_main_module.js:17:47

前三个事件被正确处理,但在第四个事件上,我们的程序崩溃了。

1[secondary_label Output]
2...
3events.js:196
4      throw er; // Unhandled 'error' event
5      ^
6
7Error: There are no more tickets left to purchase
8    at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28)
9...

第一两个行强调错误被扔掉,评论说未处理的错误`事件。如果事件发射器发出错误,而我们没有为错误事件添加听器,Node.js会扔出错误并破坏程序。

如果您正在聆听事件发射器,则始终听取错误事件的最佳做法是:如果您没有设置错误发射器,如果发射一个错误,您的整个应用程序将崩溃。

为了遵循最佳实践,让我们设置一个错误收听器. 重新打开‘index.js’文件:

1nano index.js

在我们开始购买门票之前添加一个倾听器. 请记住,倾听器只能响应被添加后的事件。

 1[label event-emitters/index.js]
 2...
 3
 4ticketManager.on("error", (error) => {
 5    console.error(`Gracefully handling our error: ${error}`);
 6});
 7
 8ticketManager.buy("[email protected]", 10);
 9ticketManager.buy("[email protected]", 10);
10ticketManager.buy("[email protected]", 10);
11ticketManager.buy("[email protected]", 10);

当我们收到错误事件时,我们将将它登录到控制台中,使用console.error()

保存并留下nano。重新运行脚本,以查看我们的错误事件被正确处理:

1node index.js

这一次,将显示以下输出:

1[secondary_label Output]
2Sending email to [email protected]
3Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293332)
4Sending email to [email protected]
5Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348)
6Sending email to [email protected]
7Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348)
8Gracefully handling our error: Error: There are no more tickets left to purchase

从最后一行,我们确认我们的错误事件正在由我们的第二个倾听器处理,并且Node.js 过程没有崩溃。

现在我们已经涵盖了发送和听取事件的概念,让我们看看一些可以用于管理事件听众的额外功能。

步骤5 – 管理事件听众

事件发射器配备了一些机制来监控和控制有多少听众订阅到一个事件. 要了解有多少听众正在处理一个事件,我们可以使用包含在每个对象中的listenerCount()方法。

listenerCount()方法接受一个参数:您想要计数的事件名称。

使用nano或您所选择的文本编辑器打开文件:

1nano index.js

您目前正在调用buy()函数四次,然后删除这四行,然后再添加这两个新行,让整个index.js看起来像这样:

 1[label event-emitters/index.js]
 2const TicketManager = require("./ticketManager");
 3const EmailService = require("./emailService");
 4const DatabaseService = require("./databaseService");
 5
 6const ticketManager = new TicketManager(3);
 7const emailService = new EmailService();
 8const databaseService = new DatabaseService();
 9
10ticketManager.on("buy", (email, price, timestamp) => {
11    emailService.send(email);
12    databaseService.save(email, price, timestamp);
13});
14
15ticketManager.on("error", (error) => {
16    console.error(`Gracefully handling our error: ${error}`);
17});
18
19console.log(`We have ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
20console.log(`We have ${ticketManager.listenerCount("error")} listener(s) for the error event`);

我们从上一节中删除了购买( )的呼叫,而不是将两个行登录到控制台上。第一个日志声明使用listenerCount()函数来显示购买( )事件的听众数目。

现在用node命令运行你的脚本:

1node index.js

你会得到这个输出:

1[secondary_label Output]
2We have 1 listener(s) for the buy event
3We have 1 listener(s) for the error event

我们只使用on()函数一次用于事件和一次用于错误事件,因此这个输出符合我们的期望。

接下来,我们将使用listenerCount(),当我们从事件播放器中删除听众时,我们可能想要删除事件听众,当事件的期限不再适用时,例如,如果我们的票券管理器被用于特定音乐会,当音乐会结束时,你会删除事件听众。

在 Node.js 中,我们使用关闭()函数来从事件发射器中删除事件收听器,该关闭()方法接受两个参数:事件名称和正在收听的函数。

<$>[注] **注:类似于 on() 函数,Node.js 以 removeListener() 代替 off() 方法。 它们都用相同的参数做同样的事情。

对于关闭()函数的第二个参数,我们需要参考正在收听事件的回复函数,因此,要删除事件收听器,必须将其回复函数保存为某个变量或常数。

要看到off()在行动中,让我们添加一个新的事件倾听器,我们将在后续呼叫中删除它。

1nano index.js

index.js的末尾,添加以下内容:

1[label event-emitters/index.js]
2...
3
4const onBuy = () => {
5    console.log("I will be removed soon");
6};

现在为购买事件添加另一个事件倾听器:

1[label event-emitters/index.js]
2...
3
4ticketManager.on("buy", onBuy);

要确保我们已经成功地添加了该事件倾听器,让我们打印购买的倾听器计数并调用购买()函数。

1[label event-emitters/index.js]
2...
3
4console.log(`We added a new event listener bringing our total count for the buy event to: ${ticketManager.listenerCount("buy")}`);
5ticketManager.buy("test@email", 20);

保存和退出文件,然后运行程序:

1node index.js

以下消息将在终端中显示:

1[secondary_label Output]
2We have 1 listener(s) for the buy event
3We have 1 listener(s) for the error event
4We added a new event listener bringing our total count for the buy event to: 2
5Sending email to test@email
6Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588814306693)
7I will be removed soon

从输出中,我们看到我们的日志声明,我们添加了新的事件倾听器,然后我们调用了买( )函数,这两个倾听者都对它做出反应。

现在让我们使用‘off()’函数来删除第二个事件倾听器. 在‘nano’中重新打开文件:

1nano index.js

现在,您还将添加一个日志声明来确认听众的数量,并再次调用到buy():

1[label event-emitters/index.js]
2...
3
4ticketManager.off("buy", onBuy);
5
6console.log(`We now have: ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
7ticketManager.buy("test@email", 20);

注意如何将onBuy变量用作off()的第二个参数。

现在用node运行此功能:

1node index.js

以前的输出将保持不变,但这一次我们会看到我们添加的新日志线,确认我们有一个听众再次。

 1[secondary_label Output]
 2We have 1 listener(s) for the buy event
 3We have 1 listener(s) for the error event
 4We added a new event listener bringing our total count for the buy event to: 2
 5Sending email to test@email
 6Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178)
 7I will be removed soon
 8We now have: 1 listener(s) for the buy event
 9Sending email to test@email
10Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178)

如果我们想删除所有事件的off(),我们可以使用removeAllListeners()函数,这个函数接受一个参数:我们想要删除听众的事件名称。

让我们在文件末尾使用此函数来取消我们为购买事件添加的第一个事件倾听器。

1nano index.js

您首先将删除所有听众使用removeAllListeners()。 然后将使用listenerCount()函数登录一个声明,然后使用listenerCount()函数登录一个声明。

在文件末尾输入以下代码:

1[label event-emitters/index.js]
2...
3
4ticketManager.removeAllListeners("buy");
5console.log(`We have ${ticketManager.listenerCount("buy")} listeners for the buy event`);
6ticketManager.buy("test@email", 20);
7console.log("The last ticket was bought");

保存和退出文件。

现在让我们用node命令执行我们的代码:

1node index.js

我们的最终产出是:

 1We have 1 listener(s) for the buy event
 2We have 1 listener(s) for the error event
 3We added a new event listener bringing our total count for the buy event to: 2
 4Sending email to test@email
 5Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
 6I will be removed soon
 7We now have: 1 listener(s) for the buy event
 8Sending email to test@email
 9Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
10We have 0 listeners for the buy event
11The last ticket was bought

删除所有听众后,我们不再发送电子邮件或保存到数据库购买门票。

结论

在本教程中,您了解了如何使用 Node.js 事件发射器来触发事件. 您使用EventEmitter对象的emit()函数发出事件,然后使用on()once()函数聆听事件执行代码,每次事件被触发,您还为错误事件添加了倾听器,并使用listenerCount()函数监控和管理听众。

有了回复和承诺,我们的票券管理系统需要与电子邮件和数据库服务模块集成,以获得相同的功能。由于我们使用了事件发射器,该事件已从实现中分离出来。此外,任何可以访问票券管理器的模块都可以观察其事件并对其做出反应。

要了解有关 Node.js 中的事件的更多信息,您可以阅读 Node.js 文档. 如果您想继续学习 Node.js,您可以返回 如何在 Node.js 系列中编码或浏览我们的 Node 主题页面上的编程项目和设置。

Published At
Categories with 技术
comments powered by Disqus