博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
node.js事件_在Node.js中使用事件发射器
阅读量:2507 次
发布时间:2019-05-11

本文共 44294 字,大约阅读时间需要 147 分钟。

node.js事件

The author selected the to receive a donation as part of the program.

作者选择了来接受捐赠,这是计划的一部分。

介绍 (Introduction)

Event emitters are objects in that trigger an event by sending a message to signal that an action was completed. developers can write code that listens to from an event emitter, allowing them to execute every time those events are triggered. In this context, events are composed of an identifying and any data that needs to be passed to the listeners.

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

Typically in Node.js, when we want to have an action occur upon completion of another action, we use techniques like nesting callbacks or chaining promises. However, these techniques tightly couple the triggering action and the resulting action, making it difficult to modify the resulting action in the future. Event emitters provide a different way to structure this relationship: the publish-subscribe pattern. In this software architecture pattern, a publisher (the event emitter) sends a message (an event), and a subscriber receives the event and performs an action. The power of this pattern is that the publisher does not need to know about the subscribers. A publisher publishes a message, and it’s up to the subscribers to react to it in their respective ways. If we wanted to change the behavior of our application, we could adjust how the subscribers react to the events without having to change the publisher.

通常在Node.js中,当我们想让一个动作在另一个动作完成时发生时,我们使用技术,例如嵌套回调或链接承诺。 但是,这些技术将触发动作和结果动作紧密地结合在一起,使得将来很难修改结果动作。 事件发射器提供了一种不同的方式来建立这种关系: 发布-订阅模式。 在这种软件体系结构模式中,发布者(事件发射器)发送消息(事件),而订阅者接收事件并执行操作。 这种模式的强大之处在于,发布者无需了解订阅者。 发布者发布一条消息,然后由订阅者以各自的方式做出响应。 如果我们想更改应用程序的行为,则可以调整订阅者对事件的React方式,而不必更改发布者。

In this article, we will create an event listener for a TicketManager that allows a user to buy tickets. We will set up listeners for the buy event, which will trigger every time a ticket is bought. This process will also show how to manage erroneous events from the emitter and how to manage event subscribers.

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

先决条件 (Prerequisites)

  • Node.js installed on your development machine. This tutorial uses version 10.20.1. To install this on macOS or Ubuntu 18.04, follow the steps in or the Installing Using a PPA section of .

    在您的开发机器上安装了Node.js。 本教程使用版本10.20.1。 要将其安装在macOS或Ubuntu 18.04上,请遵循的步骤,或 使用PPA安装部分中的 。

  • The main example in this article makes use of JavaScript classes as they were introduced in (commonly referred to as ES6). If you’d like to learn about classes in JavaScript, read our tutorial.

    本文的主要示例使用了中引入JavaScript类(通常称为ES6)。 如果您想了解JavaScript中的类,请阅读我们JavaScript中的教程。

第1步-发射事件 (Step 1 — Emitting Events)

In this step, we’ll explore the two most common ways to create an event emitter in Node.js. The first is to use an event emitter object directly, and the second is to create an object that the event emitter object.

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

Deciding which one to use depends on how coupled your events are to the actions of your objects. If the events you want to emit are an effect of an object’s actions, you would likely extend from the event emitter object to have access to its functions for convenience. If the events you want to emit are independent of your business objects or are a result of actions from many business objects, you would instead create an independent event emitter object that’s referenced by your objects.

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

Let’s begin by creating a standalone, event-emitting object. We’ll begin by creating a folder to store all of our code. In your terminal, make a new folder called event-emitters:

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

  • mkdir event-emitters

    mkdir事件发射器

Then enter that folder:

然后输入该文件夹:

  • cd event-emitters

    cd事件发射器

Open the first event emitter, firstEventEmitter.js, in a text editor. We will use nano as it’s available in the terminal:

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

  • nano firstEventEmitter.js

    纳米firstEventEmitter.js

In Node.js, we emit events via the EventEmitter class. This class is part of the events module. Let’s begin by first loading the events module in our file by adding the following line:

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

event-emitters/firstEventEmitter.js
event-emitters / firstEventEmitter.js
const { EventEmitter } = require("events");

With the class imported, we can use it to create a new object instance from it:

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

event-emitters/firstEventEmitter.js
event-emitters / firstEventEmitter.js
const { EventEmitter } = require("events");const firstEmitter = new EventEmitter();

Let’s emit an event by adding the following highlighted line at the end of firstEventEmitter.js:

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

event-emitters/firstEventEmitter.js
event-emitters / firstEventEmitter.js
const { EventEmitter } = require("events");const firstEmitter = new EventEmitter();firstEmitter.emit("My first event");

The emit() function is used to fire events. We need to pass the name of the event to it as a string. We can add any number of arguments after the event name. Events with just a name are fairly limited; the other arguments allow us to send data to our listeners. When we set up our ticket manager, our events will pass data about the purchase when it happens. Keep the name of the event in mind, because event listeners will identify it by this name.

这个emit()函数用于触发事件。 我们需要将事件的名称作为字符串传递给它。 我们可以在事件名称之后添加任意数量的参数。 仅带有名称的事件非常有限; 其他参数允许我们将数据发送给侦听器。 当我们设置票务经理时,我们的活动将在购买发生时传递有关购买的数据。 请记住事件的名称,因为事件侦听器将使用此名称进行标识。

Note: While we don’t capture it in this example, the emit() function returns true if there are listeners for the event. If there are no listeners for an event, it returns false.

注意:虽然在本示例中我们没有捕获它,但是如果有事件的侦听器, emit()函数将返回true 。 如果没有事件的侦听器,则返回false

Let’s run this file to see what happens. Save and exit nano, then execute the file with the node command:

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

  • node firstEventEmitter.js

    节点firstEventEmitter.js

When the script finishes its execution, you will see no output in the terminal. That’s because we do not log any messages in firstEventEmitter.js and there’s nothing that listens to the event that was sent. The event is emitted, but nothing acts on these events.

脚本执行完毕后,终端上将看不到任何输出。 这是因为我们没有在firstEventEmitter.js记录任何消息,并且没有任何内容可以监听已发送的事件。 事件已发出,但没有任何作用于这些事件。

Let’s work toward seeing a more complete example of publishing, listening to, and acting upon events. We’ll do this by creating a ticket manager example application. The ticket manager will expose a function to buy tickets. When a ticket is bought, an event will be sent with details of the purchaser. Later, we’ll create another Node.js module to simulate an email being sent to the purchaser’s email confirming the purchase.

让我们努力看一个更完整的发布,收听事件并对其采取行动的示例。 我们将通过创建票务管理器示例应用程序来完成此操作。 票务管理器将公开购买票证的功能。 购买门票后,将发送带有购买者详细信息的事件。 稍后,我们将创建另一个Node.js模块,以模拟发送到购买者的电子邮件以确认购买的电子邮件。

Let’s begin by creating our ticket manager. It will extend the EventEmitter class so that we don’t have to create a separate event emitter object to emit events.

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

In the same working directory, create and open a new file called ticketManager.js:

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

  • nano ticketManager.js

    纳米ticketManager.js

As with the first event emitter, we need to import the EventEmitter class from the events module. Put the following code at the beginning of the file:

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

event-emitters/ticketManager.js
event-emitters / ticketManager.js
const EventEmitter = require("events");

Now, make a new TicketManager class that will soon define the method for ticket purchases:

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

event-emitters/ticketManager.js
event-emitters / ticketManager.js
const EventEmitter = require("events");class TicketManager extends EventEmitter {}

In this case, the TicketManager class extends the EventEmitter class. This means that the TicketManager class inherits the methods and properties of the EventEmitter class. This is how it gets access to the emit() method.

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

In our ticket manager, we want to provide the initial supply of tickets that can be purchased. We’ll do this by accepting the initial supply in our , a special function that’s called when a new object of a class is made. Add the following constructor to the TicketManager class:

在我们的票务管理器中,我们希望提供可以购买的票证的初始供应。 我们将通过接受的初始供应量来完成此操作,该函数是在创建类的新对象时调用的特殊函数。 将以下构造函数添加到TicketManager类中:

event-emitters/ticketManager.js
event-emitters / ticketManager.js
const EventEmitter = require("events");class TicketManager extends EventEmitter {    constructor(supply) {        super();        this.supply = supply;    }}

The constructor has one supply argument. This is a number detailing the initial supply of tickets we can sell. Even though we declared that TicketManager is a child class of EventEmitter, we still need to call super() to get access to the methods and properties of EventEmitter. The super() function calls the constructor of the parent function, which in this case is EventEmitter.

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

Finally, we create a supply property for the object with this.supply and give it the value passed in by the constructor.

最后,我们使用this.supply为对象创建一个supply属性,并this.supply提供构造函数传递的值。

Now, let’s add a buy() method that will be called when a ticket is purchased. This method will decrease the supply by one and emit an event with the purchase data.

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

Add the buy() method as follows:

添加如下的buy()方法:

event-emitters/ticketManager.js
event-emitters / 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());    }}

In the buy() function, we take the purchaser’s email address and the price they paid for the ticket. We then decrease the supply of tickets by one. We end by emitting a buy event. This time, we emit an event with extra data: the email and price that were passed in the function as well as a timestamp of when the purchase was made.

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

So that our other can use this class, we need to export it. Add this line at the end of the file:

为了使其他可以使用此类,我们需要将其导出。 在文件末尾添加以下行:

event-emitters/ticketManager.js
event-emitters / ticketManager.js
...module.exports = TicketManager

Save and exit the file.

保存并退出文件。

We’ve finished our setup for the event emitter TicketManager. Now that we’ve put things in place to push events, we can move on to reading and processing those events. To do that, we will create event listeners in the next step.

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

第2步-听事件 (Step 2 — Listening for Events)

Node.js allows us to add a listener for an event with the on() function of an event emitter object. This listens for a particular event name and fires a callback when the event is triggered. Adding a listener typically looks like this:

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

eventEmitter.on(event_name, callback_function) {    action}

Note:: Node.js aliases the on() method with addListener(). They perform the same task. In this tutorial, we will continue to use on().

注意:: Node.js使用addListener()作为on()方法的别名。 他们执行相同的任务。 在本教程中,我们将继续使用on()

Let’s get some first-hand experience with listening to our first event. Create a new file called firstListener.js:

让我们在聆听我们的第一场比赛时获得一些第一手的经验。 创建一个名为firstListener.js的新文件:

  • nano firstListener.js

    纳米firstListener.js

As a demonstration of how the on() function works, let’s log a simple message upon receiving our first event.

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

First, let’s import TicketManager into our new Node.js module. Add the following code into firstListener.js:

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

event-emitters/firstListener.js
event-emitters / firstListener.js
const TicketManager = require("./ticketManager");const ticketManager = new TicketManager(10);

Recall that TicketManager objects need their initial supply of tickets when created. This is why we pass the 10 argument.

回想一下, TicketManager对象在创建时需要其初始票证供应。 这就是为什么我们通过10参数。

Now let’s add our first Node.js event listener. It will listen to the buy event. Add the following highlighted code:

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

event-emitters/firstListener.js
event-emitters / firstListener.js
const TicketManager = require("./ticketManager");const ticketManager = new TicketManager(10);ticketManager.on("buy", () => {    console.log("Someone bought a ticket!");});

To add a new listener, we used the on() function that’s a part of the ticketManager object. The on() method is available to all event emitter objects, and since TicketManager inherits from the EventEmitter class, this method is available on all of the TicketManager instance objects.

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

The second argument to the on() method is a callback function, written as an . The code in this function is run after the event is emitted. In this case, we log "Someone bought a ticket!" to the console if a buy event is emitted.

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

Now that we set up a listener, let’s use the buy() function so that the event will be emitted. At the end of your file add this:

现在我们已经设置了侦听器,让我们使用buy()函数,以便发出事件。 在文件末尾添加以下内容:

event-emitters/firstListener.js
event-emitters / firstListener.js
...ticketManager.buy("test@email.com", 20);

This performs the buy method with a user email of test@email.com and a ticket price of 20.

这将使用test@email.com的用户电子邮件和20的票价执行buy方法。

Save and exit the file.

保存并退出文件。

Now run this script with node:

现在使用node运行此脚本:

  • node firstListener.js

    节点firstListener.js

Your console will display this:

您的控制台将显示以下内容:

Output   
Someone bought a ticket!

Your first event listener worked. Let’s see what happens if we buy multiple tickets. Re-open your firstListener.js in your text editor:

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

  • nano firstListener.js

    纳米firstListener.js

At the end of the file, make another call to the buy() function:

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

event-emitters/firstListener.js
event-emitters / firstListener.js
...ticketManager.buy("test@email.com", 20);ticketManager.buy("test@email.com", 20);

Save and exit the file. Let’s run the script with Node.js to see what happens:

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

  • node firstListener.js

    节点firstListener.js

Your console will display this:

您的控制台将显示以下内容:

Output   
Someone bought a ticket!Someone bought a ticket!

Since the buy() function was called twice, two buy events were emitted. Our listener picked up both.

由于buy()函数被调用了两次,所以发出了两个buy事件。 我们的听众都接了。

Sometimes we’re only interested in listening to the first time an event was fired, as opposed to all the times it’s emitted. Node.js provides an alternative to on() for this case with the once() function.

有时,我们只想听事件第一次被触发,而不是一直发出。 on()这种情况下,Node.js使用Once once()函数提供了on()的替代方法。

Like on(), the once() function accepts the event name as its first argument, and a callback function that’s called when the event is fired as its second argument. Under the hood, when the event is emitted and received by a listener that uses once(), Node.js automatically removes the listener and then executes the code in the callback function.

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

Let’s see once() in action by editing firstListener.js:

让我们通过编辑firstListener.js查看once() firstListener.js once()

  • nano firstListener.js

    纳米firstListener.js

At the end of the file, add a new event listener using once() like the following highlighted lines:

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

event-emitters/firstListener.js
event-emitters / firstListener.js
const TicketManager = require("./ticketManager");const ticketManager = new TicketManager(10);ticketManager.on("buy", () => {        console.log("Someone bought a ticket!");});ticketManager.buy("test@email.com", 20);ticketManager.buy("test@email.com", 20);ticketManager.once("buy", () => {    console.log("This is only called once");});

Save and exit the file and run this program with node:

保存并退出文件,并使用node运行该程序:

  • node firstListener.js

    节点firstListener.js

The output is the same as the last time:

输出与上次相同:

Output   
Someone bought a ticket!Someone bought a ticket!

While we added a new event listener with once(), it was added after the buy events were emitted. Because of this, the listener didn’t detect these two events. You can’t listen for events that already happened in the past. When you add a listener you can only capture events that come after.

虽然我们使用after once()添加了新的事件侦听器,但在发出buy事件后才添加了它。 因此,侦听器未检测到这两个事件。 您无法收听过去已经发生的事件。 添加侦听器时,您只能捕获之后发生的事件。

Let’s add a couple more buy() function calls so we can confirm that the once() listener only reacts one time. Open firstListener.js in your text editor like before:

让我们添加几个更多的buy()函数调用,以便我们可以确认once()侦听器仅React一次。 像以前一样,在文本编辑器中打开firstListener.js

  • nano firstListener.js

    纳米firstListener.js

Add the following calls at the end of the file:

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

event-emitters/firstListener.js
event-emitters / firstListener.js
...ticketManager.once("buy", () => {    console.log("This is only called once");});ticketManager.buy("test@email.com", 20);ticketManager.buy("test@email.com", 20);

Save and exit, then execute this program:

保存并退出,然后执行以下程序:

  • node firstListener.js

    节点firstListener.js

Your output will be:

您的输出将是:

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

The first two lines were from the first two buy() calls before the once() listener was added. Adding a new event listener does not remove previous ones, so the first event listener we added is still active and logs messages.

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

Since the event listener with on() was declared before the event listener with once(), we see Someone bought a ticket! before This is only called once. These two lines are both responding to the second-to-last buy event.

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

Finally, when the last call to buy() was made, the event emitter only had the first listener that was created with on(). As mentioned earlier, when an event listener created with once() receives an event, it is automatically removed.

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

Now that we have added event listeners to detect our emitters, we will see how to capture data with those listeners.

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

步骤3 —捕获事件数据 (Step 3 — Capturing Event Data)

So far, you’ve set up event listeners to react to emitted events. The emitted events also pass along data. Let’s see how we can capture the data that accompanies an event.

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

We’ll begin by creating some new Node.js modules: an email service and a database service. They’ll be used to simulate sending an email and saving to a database respectively. We’ll then tie them all together with our main Node.js script—index.js.

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

Let’s begin by editing our email service module. Open the file in your text editor:

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

  • nano emailService.js

    纳米emailService.js

Our email service consists of a class that contains one method—send(). This method expects the email that’s emitted along with buy events. Add the following code to your file:

我们的电子邮件服务包含一个类,其中包含一个方法send() 。 此方法期望与buy事件一起发送的电子邮件。 将以下代码添加到您的文件中:

event-emitters/emailService.js
event-emitters / emailService.js
class EmailService {    send(email) {        console.log(`Sending email to ${email}`);    }}module.exports = EmailService

This code creates an EmailService class that contains a send() function. In lieu of sending an actual email, it uses to log a message to the console that would contain the email address of someone buying a ticket. Save and exit before moving on.

此代码创建一个包含send()函数的EmailService类。 代替发送实际的电子邮件,它使用将消息记录到控制台,该消息将包含购票者的电子邮件地址。 保存并退出,然后继续。

Let’s set up the database service. Open databaseService.js with your text editor:

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

  • nano databaseService.js

    纳米databaseService.js

The database service saves our purchase data to a database via its save() method. Add the following code to databaseService.js:

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

event-emitters/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

This creates a new DatabaseService class that contains a single save() method. Similar to the email service’s send() method, the save() function uses the data that accompanies a buy event, logging it to the console instead of actually inserting it into a database. This method needs the email of the purchaser, price of the ticket, and the time the ticket was purchased to function. Save and exit the file.

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

We will use our last file to bring the TicketManager, EmailService, and DatabaseService together. It will set up a listener for the buy event and will call the email service’s send() function and the database service’s save() function.

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

Open the index.js file in your text editor:

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

  • nano index.js

    纳米index.js

The first thing to do is import the modules we are using:

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

event-emitters/index.js
event-emitters / index.js
const TicketManager = require("./ticketManager");const EmailService = require("./emailService");const DatabaseService = require("./databaseService");

Next, let’s create objects for the classes we imported. We’ll set a low ticket supply of three for this demonstration:

接下来,让我们为导入的类创建对象。 对于此演示,我们将为三个人设置低价票:

event-emitters/index.js
event-emitters / 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();

We can now set up our listener with the instantiated objects. Whenever someone buys a ticket, we want to send them an email as well as saving the data to a database. Add the following listener to your code:

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

event-emitters/index.js
event-emitters / 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);});

Like before, we add a listener with the on() method. The difference this time is that we have three arguments in our callback function. Each argument corresponds to the data that the event emits. As a reminder, this is the emitter code in the buy() function:

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

event-emitters/ticketManager.js
event-emitters / ticketManager.js
this.emit("buy", email, price, Date.now());

In our callback, we first capture the email from the emitter, then the price, and finally the Date.now() data, which we capture as timestamp.

在回调中,我们首先捕获来自email ,然后捕获price ,最后捕获Date.now()数据,将其捕获为timestamp

When our listener detects a buy event, it will call the send() function from the emailService object as well as the save() function from databaseService. To test that this setup works, let’s make a call to the buy() function at the end of the file:

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

event-emitters/index.js
event-emitters / index.js
...ticketManager.buy("test@email.com", 10);

Save and exit the editor. Now let’s run this script with node and observe what comes next. In your terminal enter:

保存并退出编辑器。 现在,让我们在node上运行此脚本,然后观察下一步。 在您的终端中输入:

  • node index.js

    节点index.js

You will see the following output:

您将看到以下输出:

Output   
Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588720081832)

The data was successfully captured and returned in our callback function. With this knowledge, you can set up listeners for a variety of emitters with different event names and data. However, there are certain nuances to handling error events with event emitters.

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

Next, let’s look at how to handle error events and what standards we should follow in doing so.

接下来,让我们看看如何处理错误事件以及在执行错误事件时应遵循的标准。

步骤4 —处理错误事件 (Step 4 — Handling Error Events)

If an event emitter cannot perform its action, it should emit an event to signal that the action failed. In Node.js, the standard way for an event emitter to signal failure is by emitting an error event.

如果事件发射器无法执行其操作,则应发出事件以表明该操作失败。 在Node.js中,事件发射器发出故障信号的标准方法是发射一个error event

An error event must have its name set to error. It must also be accompanied by an Error object. Let’s see a practical example of emitting an error event.

错误事件必须将其名称设置为error 。 它还必须带有一个Error对象。 让我们看一个发出错误事件的实际例子。

Our ticket manager decreases the supply by one every time the buy() function is called. Right now there’s nothing stopping it from selling more tickets than it has available. Let’s modify the buy() function so that if the ticket supply reaches 0 and someone wants to buy a ticket, we emit an error indicating that we’re out of stock.

每次调用buy()函数时,我们的票务管理器都会将供应减少一倍。 目前,没有什么可以阻止它出售超出其可用数量的门票。 让我们修改buy()函数,以便如果票证供应量达到0并且某人想要购买票证,我们将发出一条错误消息,指示我们缺货。

Open ticketManager.js in your text editor once more:

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

  • nano ticketManager.js

    纳米ticketManager.js

Now edit the buy() function as follows:

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

event-emitters/ticketManager.js
event-emitters / 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"));}...

We’ve added an if statement that allows a ticket purchase if our supply is greater than zero. If we don’t have any other tickets, we’ll emit an error event. The error event is emitted with a new Error object that contains a description of why we’re throwing this error.

我们添加了一条if语句,如果我们的供应量大于零,则允许购买机票。 如果没有其他票证,则会发出error事件。 error事件与新的Error对象一起发出,该对象包含对我们引发此错误的原因的描述。

Save and exit the file. Let’s try to throw this error in our index.js file. Right now, we only buy one ticket. We instantiated the ticketManager object with three tickets, so we should get an error if we try to buy four tickets.

保存并退出文件。 让我们尝试在我们的index.js文件中抛出此错误。 现在,我们只买一张票。 我们用三张票证实例化了ticketManager对象,因此,如果我们尝试购买四张票证,将会出现错误。

Edit index.js with your text editor:

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

  • nano index.js

    纳米index.js

Now add the following lines at the end of the file so we can buy four tickets in total:

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

event-emitters/index.js
event-emitters / index.js
...ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);

Save and exit the editor.

保存并退出编辑器。

Let’s execute this file to see what happens:

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

  • node index.js

    节点index.js

Your output will be:

您的输出将是:

Output   
Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588724932796)Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588724932812)Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 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.
(/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:47Emitted 'error' event on TicketManager instance at: at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:14) at Object.
(/home/sammy/event-emitters/index.js:17:15) [... lines matching original stack trace ...] at internal/main/run_main_module.js:17:47

The first three buy events were processed correctly, but on the fourth buy event our program crashed. Let’s examine the beginning of the error message:

前三个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)...

The first two lines highlight that an error was thrown. The comment says "Unhandled 'error' event". If an event emitter emits an error and we did not attach a listener for error events, Node.js throws the error and crashes the program.

前两行突出显示已引发错误。 评论内容为"Unhandled 'error' event" 。 如果事件发射器发出错误,而我们没有为错误事件附加侦听器,则Node.js会引发错误并使程序崩溃。

It’s considered best practice to always listen for error events if you’re listening to an event emitter. If you do not set up a listener for errors, your entire application will crash if one is emitted. With an error listener, you can gracefully handle it.

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

To follow best practices, let’s set up a listener for errors. Re-open the index.js file:

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

  • nano index.js

    纳米index.js

Add a listener before we start buying tickets. Remember, a listener can only respond to events that are emitted after it was added. Insert an error listener like this:

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

event-emitters/index.js
event-emitters / index.js
...ticketManager.on("error", (error) => {    console.error(`Gracefully handling our error: ${error}`);});ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);ticketManager.buy("test@email.com", 10);

When we receive an error event, we will log it to the console with console.error().

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

Save and leave nano. Re-run the script to see our error event handled correctly:

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

  • node index.js

    节点index.js

This time the following output will be displayed:

这次将显示以下输出:

Output   
Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293332)Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293348)Sending email to test@email.comRunning query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293348)Gracefully handling our error: Error: There are no more tickets left to purchase

From the last line, we confirm that our error event is being handled by our second listener, and the Node.js process did not crash.

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

Now that we’ve covered the concepts of sending and listening to events, let’s look at some additional functionality that can be used to manage event listeners.

既然我们已经介绍了发送和侦听事件的概念,让我们看一下可用于管理事件侦听器的其他功能。

第5步-管理事件监听器 (Step 5 — Managing Events Listeners)

Event emitters come with some mechanisms to monitor and control how many listeners are subscribed to an event. To get an overview of how many listeners are processing an event, we can use the listenerCount() method that’s included in every object.

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

The listenerCount() method accepts one argument: the event name you want the count for. Let’s see it in action in index.js.

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

Open the file with nano or your text editor of choice:

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

  • nano index.js

    纳米index.js

You currently call the buy() function four times. Remove those four lines. When you do, add these two new lines so that your entire index.js looks like this:

您当前调用四次buy()函数。 删除这四行。 完成后,添加以下两行,以使整个index.js如下所示:

event-emitters/index.js
event-emitters / 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`);

We’ve removed the calls to buy() from the previous section and instead logged two lines to the console. The first log statement uses the listenerCount() function to display the number of listeners for the buy() event. The second log statement shows how many listeners we have for the error event.

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

Save and exit. Now run your script with the node command:

保存并退出。 现在,使用node命令运行脚本:

  • node index.js

    节点index.js

You’ll get this output:

您将获得以下输出:

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

We only used the on() function once for the buy event and once for the error event, so this output matches our expectations.

我们仅对buy事件使用一次on()函数,对于error事件使用一次,因此此输出符合我们的期望。

Next, we’ll use the listenerCount() as we remove listeners from an event emitter. We may want to remove event listeners when the period of an event no longer applies. For example, if our ticket manager was being used for a specific concert, as the concert comes to an end you would remove the event listeners.

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

In Node.js we use the off() function to remove event listeners from an event emitter. The off() method accepts two arguments: the event name and the function that’s listening to it.

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

Note: Similar to the on() function, Node.js aliases the off() method with removeListener(). They both do the same thing, with the same arguments. In this tutorial, we will continue to use off().

注意 :与on()函数类似,Node.js使用removeListener()作为off()方法的别名。 他们都用相同的论点做同样的事情。 在本教程中,我们将继续使用off()

For the second argument of the off() function, we need a reference to the callback that’s listening to an event. Therefore, to remove an event listener, its callback must be saved to some variable or constant. As it stands, we cannot remove the current event listeners for buy or error with the off() function.

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

To see off() in action, let’s add a new event listener that we will remove in subsequent calls. First, let’s define the callback in a variable so that we can reference it in off() later. Open index.js with nano:

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

  • nano index.js

    纳米index.js

At the end of index.js add this:

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

event-emitters/index.js
event-emitters / index.js
...const onBuy = () => {    console.log("I will be removed soon");};

Now add another event listener for the buy event:

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

event-emitters/index.js
event-emitters / index.js
...ticketManager.on("buy", onBuy);

To be sure that we successfully added that event listener, let’s print the listener count for buy and call the buy() function.

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

event-emitters/index.js
event-emitters / 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);

Save and exit the file, then run the program:

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

  • node index.js

    节点index.js

The following message will be displayed in the terminal:

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

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

From the output, we see our log statement from when we added the new event listener. We then call the buy() function, and both listeners react to it. The first listener sent the email and saved data to the database, and then our second listener printed its message I will be removed soon to the screen.

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

Let’s now use the off() function to remove the second event listener. Re-open the file in nano:

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

  • nano index.js

    纳米index.js

Now add the following off() call to the end of the file. You will also add a log statement to confirm the number of listeners, and make another call to buy():

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

event-emitters/index.js
event-emitters / 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);

Note how the onBuy variable was used as the second argument of off(). Save and exit the file.

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

Now run this with node:

现在使用node运行它:

  • node index.js

    节点index.js

The previous output will remain unchanged, but this time we will find the new log line we added confirming we have one listener once more. When buy() is called again, we will only see the output of the callback used by the first listener:

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

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

If we wanted to remove all events with off(), we could use the removeAllListeners() function. This function accepts one argument: the name of the event we want to remove listeners for.

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

Let’s use this function at the end of the file to take off the first event listener we added for the buy event. Open the index.js file once more:

让我们在文件末尾使用此功能来删除为buy事件添加的第一个事件侦听器。 再次打开index.js文件:

  • nano index.js

    纳米index.js

You’ll first remove all the listeners with removeAllListeners(). You’ll then log a statement with the listener count using the listenerCount() function. To confirm it’s no longer listening, you’ll buy another ticket. When the event is emitted, nothing will react to it.

首先,使用removeAllListeners()删除所有侦听器。 然后,您将使用listenerCount()函数记录一个包含侦听器计数的语句。 为了确认不再收听,您将购买另一张票。 发出事件时,没有任何React。

Enter the following code at the end of the file:

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

event-emitters/index.js
event-emitters / 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");

Save and exit the file.

保存并退出文件。

Now let’s execute our code with the node command:

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

  • node index.js

    节点index.js

Our final output is:

我们的最终输出是:

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

After removing all listeners, we no longer send emails or save to the database for ticket purchases.

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

结论 (Conclusion)

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事件发射器来触发事件。 你发出的事件与emit()的函数EventEmitter对象,然后听取了与事件on()once()函数来执行代码每次触发事件的时间。 您还添加了一个错误事件的侦听器,并使用listenerCount()函数监视和管理了listenerCount()

With callbacks and promises, our ticket manager system would need to be integrated with the email and database service modules to get the same functionality. Since we used event emitters, the event was decoupled from the implementations. Furthermore, any module with access to the ticket manager can observe its event and react to it. If you want Node.js modules, internal or external, to observe what’s happening with your object, consider making it an event emitter for scalability.

有了回调和承诺,我们的票务管理器系统将需要与电子邮件和数据库服务模块集成以获得相同的功能。 由于我们使用了事件发射器,因此事件与实现分离了。 此外,任何有权访问票务管理器的模块都可以观察其事件并做出React。 如果希望内部或外部的Node.js模块观察对象的状况,请考虑使其成为可伸缩性的事件发射器。

To learn more about events in Node.js, you can read the . If you’d like to continue learning Node.js, you can return to the , or browse programming projects and setups on our .

要了解有关Node.js中事件的更多信息,请阅读 。 如果您想继续学习Node.js,可以返回“ ,或者在上浏览编程项目和设置。

翻译自:

node.js事件

转载地址:http://fphgb.baihongyu.com/

你可能感兴趣的文章
NSTextView 文字链接的定制化
查看>>
第五天站立会议内容
查看>>
CentOs7安装rabbitmq
查看>>
(转))iOS App上架AppStore 会遇到的坑
查看>>
解决vmware与主机无法连通的问题
查看>>
做好产品
查看>>
项目管理经验
查看>>
笔记:Hadoop权威指南 第8章 MapReduce 的特性
查看>>
JMeter响应数据出现乱码的处理-三种解决方式
查看>>
获取设备实际宽度
查看>>
图的算法专题——最短路径
查看>>
SQL批量删除与批量插入
查看>>
Notes on <High Performance MySQL> -- Ch3: Schema Optimization and Indexing
查看>>
C语言之一般树
查看>>
懂了很多大道理,却依旧过不好一生
查看>>
手工数据结构系列-C语言模拟队列 hdu1276
查看>>
【PyQt5 学习记录】008:改变窗口样式之二
查看>>
android EditText长按屏蔽ActionMode context菜单但保留选择工具功能
查看>>
BUAA 111 圆有点挤
查看>>
c++ 继承产生的名字冲突问题 (1)
查看>>