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

作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。

介绍

事件发射器Node.js中的对象,它们通过发送消息来表示操作已完成来触发事件。JavaScript的开发人员可以编写代码侦听事件从事件发射器,使他们能够执行的功能每一个这些事件被触发的时间。在此上下文中,事件由标识字符串和需要传递给侦听器的任何数据组成

通常在 Node.js 中,当我们想要在另一个动作完成时发生一个动作时,我们使用异步编程技术,如嵌套回调或链接承诺。然而,这些技术将触发动作和结果动作紧密耦合,使得将来很难修改结果动作。事件发射器提供了一种不同的方式来构建这种关系:发布-订阅图案。在此软件架构模式中,发布者(事件发射器)发送消息(事件),订阅者接收事件并执行操作。这种模式的强大之处在于发布者不需要知道订阅者。发布者发布消息,订阅者以各自的方式对其做出反应。如果我们想改变应用程序的行为,我们可以调整订阅者对事件的反应,而不必更改发布者。

在本文中,我们将为允许用户购买门票TicketManager JavaScript 类创建一个事件侦听器我们将为buy事件设置侦听器,每次购买门票时都会触发。此过程还将展示如何管理来自发射器的错误事件以及如何管理事件订阅者。

先决条件

第 1 步 – 发出事件

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

决定使用哪一个取决于您的事件与您的对象的操作的耦合程度。如果要发出的事件是对象操作的效果,为了方便起见,您可能会从事件发射器对象扩展以访问其功能。如果您要发出的事件独立于您的业务对象,或者是来自多个业务对象的操作的结果,您将改为创建一个由您的对象引用的独立事件发射器对象。

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

  • mkdir event-emitters

然后进入该文件夹:

  • cd event-emitters

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

  • nano firstEventEmitter.js

在 Node.js 中,我们通过EventEmitter发出事件这个类是events模块的一部分让我们首先通过events添加以下行将模块加载到我们的文件中:

事件发射器/firstEventEmitter.js
const { EventEmitter } = require("events");

导入类后,我们可以使用它从它创建一个新的对象实例:

事件发射器/firstEventEmitter.js
const { EventEmitter } = require("events");

const firstEmitter = new EventEmitter();

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

事件发射器/firstEventEmitter.js
const { EventEmitter } = require("events");

const firstEmitter = new EventEmitter();


firstEmitter.emit("My first event");

emit()函数用于触发事件。我们需要将事件的名称作为字符串传递给它。我们可以在事件名称后添加任意数量的参数。只有名字的事件相当有限;其他参数允许我们向我们的听众发送数据。当我们设置我们的票务管理器时,我们的事件将在发生时传递有关购买的数据。请记住事件的名称,因为事件侦听器将通过此名称识别它。

注意:虽然我们在这个例子中没有捕获它,但如果有事件的监听器,emit()函数就会返回true如果事件没有侦听器,则返回false

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

  • node firstEventEmitter.js

脚本完成执行后,您将在终端中看不到任何输出。那是因为我们没有记录任何消息firstEventEmitter.js,也没有任何东西可以监听发送的事件。事件被发出,但对这些事件没有任何作用。

让我们努力查看更完整的发布、监听和处理事件的示例。我们将通过创建一个工单管理器示例应用程序来实现这一点。票务管理器会公开一个买票的函数。购买门票后,将发送包含购买者详细信息的事件。稍后,我们将创建另一个 Node.js 模块来模拟向购买者的电子邮件发送确认购买的电子邮件。

让我们从创建我们的票务管理器开始。它将扩展EventEmitter该类,以便我们不必创建单独的事件发射器对象来发射事件。

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

  • nano ticketManager.js

与第一个事件发射器一样,我们需要EventEmitterevents模块中导入将以下代码放在文件的开头:

事件发射器/ticketManager.js
const EventEmitter = require("events");

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

事件发射器/ticketManager.js
const EventEmitter = require("events");

class TicketManager extends EventEmitter {}

在这种情况下,TicketManager该类扩展了EventEmitter该类。这意味着TicketManager该类继承该类的方法和属性EventEmitter这就是它如何访问该emit()方法。

在我们的票务管理器中,我们希望提供可以购买的门票的初始供应量。我们将通过在我们的 中接受初始供应来做到这一点,constructor()这是一个在创建类的新对象时调用的特殊函数。将以下构造函数添加到TicketManager类中:

事件发射器/ticketManager.js
const EventEmitter = require("events");

class TicketManager extends EventEmitter {
    constructor(supply) {
        super();
        this.supply = supply;
    }
}

构造函数只有一个supply参数。这是一个详细说明我们可以出售的门票的初始供应量的数字。即使我们声明它TicketManager是 的子类EventEmitter,我们仍然需要调用super()来访问 的方法和属性EventEmittersuper()函数调用父函数的构造函数,在本例中为EventEmitter

最后,我们supply为对象创建一个属性,this.supply并为其赋予构造函数传入的值。

现在,让我们添加一个buy()将在购买机票时调用方法。此方法将供应量减少一并发出带有购买数据的事件。

添加buy()方法如下:

事件发射器/ticketManager.js
const EventEmitter = require("events");

class TicketManager extends EventEmitter {
    constructor(supply) {
        super();
        this.supply = supply;
    }

    buy(email, price) {
        this.supply--;
        this.emit("buy", email, price, Date.now());
    }
}

buy()函数中,我们获取购买者的电子邮件地址和他们为机票支付的价格。然后我们将门票的供应量减少一张。我们以发出一个buy事件结束这一次,我们发出一个带有额外数据的事件:在函数中传递的电子邮件和价格以及购买时间的时间戳。

为了让我们的其他Node.js 模块可以使用这个类,我们需要导出它。在文件末尾添加这一行:

事件发射器/ticketManager.js
...

module.exports = TicketManager

保存并退出文件。

我们已经完成了事件发射器的设置TicketManager现在我们已经准备好推送事件,我们可以继续读取和处理这些事件。为此,我们将在下一步中创建事件侦听器。

第 2 步 – 监听事件

Node.js 允许我们on()使用事件发射器对象功能为事件添加侦听器。这会侦听特定的事件名称并在触发事件时触发回调。添加侦听器通常如下所示:

eventEmitter.on(event_name, callback_function) {
    action
}

注意: : Node.json()使用addListener(). 他们执行相同的任务。在本教程中,我们将继续使用on().

让我们通过聆听我们的第一个活动获得一些第一手经验。创建一个名为 的新文件firstListener.js

  • nano firstListener.js

为了演示该on()函数的工作原理,让我们在接收到第一个事件时记录一条简单的消息。

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

事件发射器/firstListener.js
const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

回想一下,TicketManager对象在创建时需要它们的初始票证供应。这就是我们传递10参数的原因。

现在让我们添加我们的第一个 Node.js 事件侦听器。它将监听buy事件。添加以下突出显示的代码:

事件发射器/firstListener.js
const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

ticketManager.on("buy", () => {
    console.log("Someone bought a ticket!");
});

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

on()方法的第二个参数是回调函数,写成箭头函数此函数中的代码在事件发出后运行。在这种情况下,"Someone bought a ticket!"如果发出buy事件,我们会登录到控制台

现在我们设置了一个侦听器,让我们使用该buy()函数来发出事件。在您的文件末尾添加以下内容:

事件发射器/firstListener.js
...

ticketManager.buy("[email protected]", 20);

这将buy使用用户电子邮件[email protected]和门票价格执行该方法20

保存并退出文件。

现在运行这个脚本node

  • node firstListener.js

您的控制台将显示:

Output
Someone bought a ticket!

你的第一个事件监听器起作用了。让我们看看如果我们购买多张票会发生什么。firstListener.js在文本编辑器中重新打开

  • nano firstListener.js

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

事件发射器/firstListener.js
...

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

保存并退出文件。让我们用 Node.js 运行脚本看看会发生什么:

  • node firstListener.js

您的控制台将显示:

Output
Someone bought a ticket! Someone bought a ticket!

由于该buy()函数被调用了两次,buy因此发出了两个事件。我们的听众两个都听懂了。

有时我们只对第一次触发事件感兴趣,而不是它发出的所有时间。Node.json()为这种情况提供了一种替代once()方法。

像 一样on(),该once()函数接受事件名称作为其第一个参数,以及一个在事件被触发时调用的回调函数作为其第二个参数。在后台,当事件由使用 的侦听器发出和接收时once(),Node.js 会自动移除侦听器,然后执行回调函数中的代码。

让我们once()通过编辑来看看实际情况firstListener.js

  • nano firstListener.js

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

事件发射器/firstListener.js
const TicketManager = require("./ticketManager");

const ticketManager = new TicketManager(10);

ticketManager.on("buy", () => {
        console.log("Someone bought a ticket!");
});

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

ticketManager.once("buy", () => {
    console.log("This is only called once");
});

保存并退出文件并使用以下命令运行此程序node

  • node firstListener.js

输出与上次相同:

Output
Someone bought a ticket! Someone bought a ticket!

虽然我们添加了一个新的事件侦听器once(),但它是在buy事件发出后添加的因此,侦听器没有检测到这两个事件。你不能监听过去已经发生的事件。添加侦听器时,您只能捕获之后发生的事件。

让我们再添加几个buy()函数调用,以便确认once()监听器只响应一次。firstListener.js像以前一样在文本编辑器中打开

  • nano firstListener.js

在文件末尾添加以下调用:

事件发射器/firstListener.js
...

ticketManager.once("buy", () => {
    console.log("This is only called once");
});

ticketManager.buy("[email protected]", 20);
ticketManager.buy("[email protected]", 20);

保存退出,然后执行这个程序:

  • node firstListener.js

您的输出将是:

Output
Someone bought a ticket! Someone bought a ticket! Someone bought a ticket! This is only called once Someone bought a ticket!

前两行来自添加侦听器buy()之前的前两次调用once()添加新的事件侦听器不会删除以前的事件侦听器,因此我们添加的第一个事件侦听器仍处于活动状态并记录消息。

由于事件侦听器 withon()是在事件侦听器 with 之前声明的once(),因此我们看到Someone bought a ticket!before This is only called once这两行都响应倒数第二个buy事件。

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

现在我们已经添加了事件侦听器来检测我们的发射器,我们将看到如何使用这些侦听器捕获数据。

第 3 步 – 捕获事件数据

到目前为止,您已经设置了事件侦听器来对发出的事件做出反应。发出的事件还会传递数据。让我们看看如何捕获事件伴随的数据。

我们将首先创建一些新的 Node.js 模块:电子邮件服务和数据库服务。它们将分别用于模拟发送电子邮件和保存到数据库。然后,我们将使用我们的主要 Node.js 脚本将它们全部绑定在一起index.js

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

  • nano emailService.js

我们的电子邮件服务包含一个包含一个方法的类—— send()此方法需要随buy事件一起发出的电子邮件将以下代码添加到您的文件中:

事件发射器/emailService.js
class EmailService {
    send(email) {
        console.log(`Sending email to ${email}`);
    }
}

module.exports = EmailService

此代码创建一个EmailService包含send()函数代替发送实际电子邮件,它使用模板文字将消息记录到控制台,该消息将包含购买机票的人的电子邮件地址。在继续之前保存并退出。

让我们设置数据库服务。databaseService.js用你的文本编辑器打开

  • nano databaseService.js

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

事件发射器/databaseService.js
class DatabaseService {
    save(email, price, timestamp) {
        console.log(`Running query: INSERT INTO orders VALUES (email, price, created) VALUES (${email}, ${price}, ${timestamp})`);
    }
}

module.exports = DatabaseService

这将创建一个DatabaseService包含单个save()方法的新与电子邮件服务的send()方法类似,该save()函数使用buy事件附带的数据,将其记录到控制台,而不是实际将其插入数据库。此方法需要购买者的电子邮件、机票价格和购买机票的时间才能起作用。保存并退出文件。

我们将用我们的最后一个文件带来的TicketManagerEmailServiceDatabaseService在一起。它将为该buy事件设置一个侦听器,并将调用电子邮件服务的send()函数和数据库服务的save()函数。

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

  • nano index.js

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

事件发射器/index.js
const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

接下来,让我们为导入的类创建对象。我们将为这次演示设置三个的低门票供应:

事件发射器/index.js
const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();

我们现在可以使用实例化的对象设置我们的侦听器。每当有人买票时,我们都希望向他们发送电子邮件并将数据保存到数据库中。将以下侦听器添加到您的代码中:

事件发射器/index.js
const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();


ticketManager.on("buy", (email, price, timestamp) => {
    emailService.send(email);
    databaseService.save(email, price, timestamp);
});

像以前一样,我们使用该on()方法添加一个侦听器这次的不同之处在于我们的回调函数中有三个参数。每个参数对应于事件发出的数据。提醒一下,这是buy()函数中的发射器代码

事件发射器/ticketManager.js
this.emit("buy", email, price, Date.now());

在我们的回调中,我们首先email从发射器捕获,然后是price,最后是Date.now()我们捕获为数据timestamp

当我们的侦听器检测到一个buy事件时,它将send()emailService对象调用函数以及调用save()函数databaseService为了测试这个设置是否有效,让我们调用buy()文件末尾函数:

事件发射器/index.js
...

ticketManager.buy("[email protected]", 10);

保存并退出编辑器。现在让我们运行这个脚本node并观察接下来会发生什么。在您的终端中输入:

  • node index.js

您将看到以下输出:

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

数据被成功捕获并在我们的回调函数中返回。有了这些知识,您可以为具有不同事件名称和数据的各种发声器设置侦听器。但是,使用事件发射器处理错误事件有一些细微差别。

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

第 4 步 – 处理错误事件

如果事件发射器不能执行它的动作,它应该发出一个事件来表示动作失败。在 Node.js 中,事件发射器发出故障信号的标准方法是发射错误事件

错误事件的名称必须设置为error它也必须伴随一个Error对象。让我们看一个发出错误事件的实际示例。

每次buy()调用函数时,我们的票务管理器都会将供应量减少 1 现在,没有什么能阻止它售出的票数超过可用票数。让我们修改buy()函数,以便如果门票供应达到0并且有人想买票,我们会发出一个错误,指示我们缺货。

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

  • nano ticketManager.js

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

事件发射器/ticketManager.js
...

buy(email, price) {
    if (this.supply > 0) {
        this.supply—;
        this.emit("buy", email, price, Date.now());
        return;
    }

    this.emit("error", new Error("There are no more tickets left to purchase"));
}
...

我们添加了一个if声明,如果我们的供应量大于零,则允许购买机票。如果我们没有任何其他票证,我们将发出一个error事件。error事件与一个新Error对象一起发出,该对象包含我们为何抛出此错误的描述。

保存并退出文件。让我们尝试在我们的index.js文件中抛出这个错误现在,我们只买一张票。我们ticketManager用三张票实例化了对象,所以如果我们尝试购买四张票,我们应该会得到一个错误。

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

  • nano index.js

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

事件发射器/index.js
...

ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);

保存并退出编辑器。

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

  • node index.js

您的输出将是:

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

前三个buy事件被正确处理,但在第四个buy事件中我们的程序崩溃了。让我们检查错误消息的开头:

Output
... events.js:196 throw er; // Unhandled 'error' event ^ Error: There are no more tickets left to purchase at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28) ...

前两行突出显示了一个错误被抛出。评论说"Unhandled 'error' event"如果事件发射器发出错误并且我们没有为错误事件附加侦听器,Node.js 会抛出错误并使程序崩溃。

error如果您正在侦听事件发射器,则始终侦听事件被认为是最佳实践如果您没有为错误设置侦听器,那么您的整个应用程序将在发出一个时崩溃。使用错误侦听器,您可以优雅地处理它。

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

  • nano index.js

在我们开始买票之前添加一个听众。请记住,侦听器只能响应添加后发出的事件。插入一个错误监听器,如下所示:

事件发射器/index.js
...

ticketManager.on("error", (error) => {
    console.error(`Gracefully handling our error: ${error}`);
});

ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);
ticketManager.buy("[email protected]", 10);

当我们收到错误事件时,我们会将其记录到控制台console.error()

保存并离开nano重新运行脚本以查看正确处理的错误事件:

  • node index.js

这次将显示以下输出:

Output
Sending email to [email protected] Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293332) Sending email to [email protected] Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348) Sending email to [email protected] Running query: INSERT INTO orders VALUES (email, price, created) VALUES ([email protected], 10, 1588726293348) Gracefully handling our error: Error: There are no more tickets left to purchase

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

现在我们已经介绍了发送和侦听事件的概念,让我们看看一些可用于管理事件侦听器的附加功能。

第 5 步 – 管理事件监听器

事件发射器带有一些机制来监视和控制订阅事件的侦听器数量。要大致了解有多少侦听器正在处理一个事件,我们可以使用listenerCount()包含在每个对象中的方法。

listenerCount()方法接受一个参数:您想要计数的事件名称。让我们看看它在index.js.

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

  • nano index.js

您当前调用该buy()函数四次。删除这四行。当你这样做时,添加这两行新行,让你的整个index.js看起来像这样:

事件发射器/index.js
const TicketManager = require("./ticketManager");
const EmailService = require("./emailService");
const DatabaseService = require("./databaseService");

const ticketManager = new TicketManager(3);
const emailService = new EmailService();
const databaseService = new DatabaseService();

ticketManager.on("buy", (email, price, timestamp) => {
    emailService.send(email);
    databaseService.save(email, price, timestamp);
});

ticketManager.on("error", (error) => {
    console.error(`Gracefully handling our error: ${error}`);
});

console.log(`We have ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
console.log(`We have ${ticketManager.listenerCount("error")} listener(s) for the error event`);

我们buy()从上一节中删除了对 的调用,而是将两行记录到控制台。第一个日志语句使用该listenerCount()函数显示buy()事件的侦听器数量第二个日志语句显示了我们有多少个error事件监听器

保存并退出。现在使用以下node命令运行您的脚本

  • node index.js

你会得到这个输出:

Output
We have 1 listener(s) for the buy event We have 1 listener(s) for the error event

我们只on()buy事件使用了一次函数,对事件使用了一次error,所以这个输出符合我们的预期。

接下来,我们将使用 ,listenerCount()因为我们从事件发射器中删除侦听器。当事件的周期不再适用时,我们可能希望删除事件侦听器。例如,如果我们的票务管理器被用于特定的音乐会,当音乐会结束时,您将删除事件侦听器。

在 Node.js 中,我们使用该off()函数从事件发射器中移除事件侦听器。off()方法接受两个参数:事件名称和侦听它的函数。

注意:与on()函数类似,Node.jsoff()使用removeListener(). 他们都做同样的事情,有着同样的论点。在本教程中,我们将继续使用off().

对于off()函数的第二个参数,我们需要对侦听事件的回调的引用。因此,要删除事件侦听器,必须将其回调保存到某个变量或常量。就目前而言,我们无法删除函数的当前事件侦听器buyerror使用该off()函数删除当前事件监听器

要查看off()实际效果,让我们添加一个新的事件侦听器,我们将在后续调用中删除该侦听器。首先,让我们在一个变量中定义回调,以便我们off()以后可以引用它打开index.js方式nano

  • nano index.js

在最后index.js添加这个:

事件发射器/index.js
...

const onBuy = () => {
    console.log("I will be removed soon");
};

现在为该事件添加另一个事件侦听器buy

事件发射器/index.js
...

ticketManager.on("buy", onBuy);

为了确保我们成功添加了该事件侦听器,让我们打印侦听器计数buy并调用该buy()函数。

事件发射器/index.js
...

console.log(`We added a new event listener bringing our total count for the buy event to: ${ticketManager.listenerCount("buy")}`);
ticketManager.buy("test@email", 20);

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

  • node index.js

终端中将显示以下消息:

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

从输出中,我们可以看到添加新事件侦听器时的日志语句。然后我们调用该buy()函数,两个侦听器都对它做出反应。第一个侦听器将电子邮件和保存的数据发送到数据库,然后我们的第二个侦听器将其消息打印I will be removed soon到屏幕上。

现在让我们使用该off()函数删除第二个事件侦听器。重新打开文件nano

  • nano index.js

现在将以下off()调用添加到文件末尾。您还将添加一条日志语句来确认侦听器的数量,并再次调用buy()

事件发射器/index.js
...

ticketManager.off("buy", onBuy);

console.log(`We now have: ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
ticketManager.buy("test@email", 20);

请注意onBuy变量是如何用作 的第二个参数的off()保存并退出文件。

现在运行它node

  • node index.js

之前的输出将保持不变,但这次我们将找到我们添加的新日志行,确认我们再次拥有一个监听器。buy()被再次调用,我们只看到第一个监听使用回调的输出:

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

如果我们想用 删除所有事件off(),我们可以使用该removeAllListeners()函数。此函数接受一个参数:我们要为其移除侦听器的事件的名称。

让我们在文件末尾使用这个函数来取消我们为buy事件添加的第一个事件监听器index.js再次打开文件:

  • nano index.js

您将首先使用 删除所有侦听器removeAllListeners()然后,您将使用该listenerCount()函数记录带有侦听器计数的语句要确认它不再收听,您将再买一张票。当事件发出时,没有任何反应。

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

事件发射器/index.js
...

ticketManager.removeAllListeners("buy");
console.log(`We have ${ticketManager.listenerCount("buy")} listeners for the buy event`);
ticketManager.buy("test@email", 20);
console.log("The last ticket was bought");

保存并退出文件。

现在让我们使用以下node命令执行我们的代码

  • node index.js

我们的最终输出是:

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

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

结论

In this tutorial, you learned how to use Node.js event emitters to trigger events. You emitted events with the emit() function of an EventEmitter object, then listened to events with the on() and once() functions to execute code every time the event is triggered. You also added a listener for an error event and monitored and managed listeners with the listenerCount() function.

通过回调和承诺,我们的工单管理系统需要与电子邮件和数据库服务模块集成以获得相同的功能。由于我们使用了事件发射器,事件与实现分离。此外,任何可以访问工单管理器的模块都可以观察其事件并对其做出反应。如果您希望 Node.js 模块(内部或外部)观察对象发生的情况,请考虑将其设置为事件发射器以实现可扩展性。

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

觉得文章有用?

点个广告表达一下你的爱意吧 !😁