How to use EventEmitter to handle events in Node.js?

青灯夜游
Release: 2020-11-02 17:46:09
forward
2482 people have browsed it

How to use EventEmitter to handle events in Node.js?

In this tutorial we learn about Node.js’ nativeEvenEmitterclass. After finishing the course, you will understand events, how to useEvenEmitter, and how to utilize events in your programs. In addition, you will also learn how theEventEmitterclass extends from other local modules, and understand the principles behind it through some examples.

Recommended tutorial:node js tutorial

In short, this article covers everything about theEventEmitterclass.

What is an event?

Event-driven architecture is very common today. Event-driven programs can generate, detect and respond to various events.

The core part of Node.js is event-driven, and many modules such as the file system (fs) andstreamthemselves useEventEmitterWritten by.

In event-driven programming,Event (event)is the result of one or more actions, which may be the user's operation or the timing output of the sensor, etc.

We can think of event-driven programs as a publish-subscribe model, where the publisher triggers events and the subscribers listen to the events and take appropriate actions.

For example, suppose there is a server to which users can upload images. In event-driven programming, an action such as uploading an image will emit an event, which will also have 1 to n subscribers in order to take advantage of it.

After the upload event is triggered, a subscriber can let them know and react to the site's administrator by emailing the site's administrator; another subscriber may collect information about the action , and save it in the database.

These events are usually independent of each other, although they may also be dependent on each other.

What is EventEmitter? The

EventEmitterclass is a built-in class of Node.js, located in theeventsmodule. According to the description in the documentation:

Most of the Node.js core API is implemented based on an idiomatic asynchronous event-driven architecture, in which certain types of objects (called " Emitter") emits named events that cause calls to Functionobjects ("listeners")"

This class can be described to some extent as a publish-subscribe model The implementation of auxiliary tools, because it can help event emitters (publishers) publish events (messages) to listeners (subscribers) in a simple way.

Create EventEmitters

Having said that , but it is more practical to create anEventEmitterfirst. This can be done by creating an instance of the class itself or implementing it through a custom class, and then creating an instance of the class.

CreateEventEmitterObject

Start with a simple example: Create anEventEmitterthat emits an event containing program running time information every second.

First start witheventsImport theEventEmitterclass in the module:

const { EventEmitter } = require('events');
Copy after login

Then create anEventEmitter:

const timerEventEmitter = new EventEmitter();
Copy after login

Use this object to publish events. Easy:

timerEventEmitter.emit("update");
Copy after login

The event name has been specified previously and it was published as an event. But the program did not respond because no listener has reacted to this event yet.

Let first This event repeats once every second. Use thesetInterval()method to create a timer and publish theupdateevent once every second:

let currentTime = 0; // 每秒触发一次 update 事件 setInterval(() => { currentTime++; timerEventEmitter.emit('update', currentTime); }, 1000);
Copy after login

EventEmitterThe instance is used to accept event names and parameters. Passupdateas the event name andcurrentTimeas the time since the program started.

Passemit( )method triggers the emitter, which pushes the event with the information we provide. Once the event emitter is ready, subscribe an event listener to it:

timerEventEmitter.on('update', (time) => { console.log('从发布者收到的消息:'); console.log(`程序已经运行了 ${time} 秒`); });
Copy after login

viaon()Method creates a listener, passing the event name to specify which event you want the listener to be attached to. On theupdateevent, runs a method that records the time.

on()The second parameter of the function is a callback that can accept additional data emitted by the event.

Running the code will output:

从发布者收到的消息: 程序已经运行了 1 秒 从发布者收到的消息: 程序已经运行了 2 秒 从发布者收到的消息: 程序已经运行了 3 秒 ...
Copy after login

If only when the event is triggered for the first time Only when you need to perform certain operations, you can also subscribe using theonce()method:

timerEventEmitter.once('update', (time) => { console.log('从发布者收到的消息:'); console.log(`程序已经运行了 ${time} 秒`); });
Copy after login

Running this code will output:

从发布者收到的消息: 程序已经运行了 1 秒
Copy after login

EventEmitterWith multiple listeners

Create another event sender below. This is a timer program with three listeners. The first listener updates the time every second, the second listener fires when the timer is about to end, and the last one fires when the timer ends:

  • update: every second Trigger once
  • end: Trigger when the countdown ends
  • end-soon: Trigger 2 seconds before the end of the countdown

First write a function to create this event emitter:

const countDown = (countdownTime) => { const eventEmitter = new EventEmitter(); let currentTime = 0; // 每秒触发一次 update 事件 const timer = setInterval(() => { currentTime++; eventEmitter.emit('update', currentTime); // 检查计时是否已经结束 if (currentTime === countdownTime) { clearInterval(timer); eventEmitter.emit('end'); } // 检查计时是否会在 2 秒后结束 if (currentTime === countdownTime - 2) { eventEmitter.emit('end-soon'); } }, 1000); return eventEmitter; };
Copy after login

这个函数启动了一个每秒钟发出一次update事件的事件。

第一个if用来检查计时是否已经结束并停止基于间隔的事件。如果已结束将会发布end事件。

如果计时没有结束,那么就检查计时是不是离结束还有 2 秒,如果是则发布end-soon事件。

向该事件发射器添加一些订阅者:

const myCountDown = countDown(5); myCountDown.on('update', (t) => { console.log(`程序已经运行了 ${t} 秒`); }); myCountDown.on('end', () => { console.log('计时结束'); }); myCountDown.on('end-soon', () => { console.log('计时将在2秒后结束'); });
Copy after login

这段代码将会输出:

程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束
Copy after login
Copy after login

扩展EventEmitter

接下来通过扩展EventEmitter类来实现相同的功能。首先创建一个处理事件的CountDown类:

const { EventEmitter } = require('events'); class CountDown extends EventEmitter { constructor(countdownTime) { super(); this.countdownTime = countdownTime; this.currentTime = 0; } startTimer() { const timer = setInterval(() => { this.currentTime++; this.emit('update', this.currentTime); // 检查计时是否已经结束 if (this.currentTime === this.countdownTime) { clearInterval(timer); this.emit('end'); } // 检查计时是否会在 2 秒后结束 if (this.currentTime === this.countdownTime - 2) { this.emit('end-soon'); } }, 1000); } }
Copy after login

可以在类的内部直接使用this.emit()。另外startTimer()函数用于控制计时开始的时间。否则它将在创建对象后立即开始计时。

创建一个CountDown的新对象并订阅它:

const myCountDown = new CountDown(5); myCountDown.on('update', (t) => { console.log(`计时开始了 ${t} 秒`); }); myCountDown.on('end', () => { console.log('计时结束'); }); myCountDown.on('end-soon', () => { console.log('计时将在2秒后结束'); }); myCountDown.startTimer();
Copy after login

运行程序会输出:

程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束
Copy after login
Copy after login

on()函数的别名是addListener()。看一下end-soon事件监听器:

myCountDown.on('end-soon', () => { console.log('计时将在2秒后结束'); });
Copy after login

也可以用addListener()来完成相同的操作,例如:

myCountDown.addListener('end-soon', () => { console.log('计时将在2秒后结束'); });
Copy after login

EventEmitter的主要函数

eventNames()

此函数将以数组形式返回所有活动的侦听器名称:

const myCountDown = new CountDown(5); myCountDown.on('update', (t) => { console.log(`程序已经运行了 ${t} 秒`); }); myCountDown.on('end', () => { console.log('计时结束'); }); myCountDown.on('end-soon', () => { console.log('计时将在2秒后结束'); }); console.log(myCountDown.eventNames());
Copy after login

运行这段代码会输出:

[ 'update', 'end', 'end-soon' ]
Copy after login

如果要订阅另一个事件,例如myCount.on('some-event', ...),则新事件也会添加到数组中。

这个方法不会返回已发布的事件,而是返回订阅的事件的列表。

removeListener()

这个函数可以从EventEmitter中删除已订阅的监听器:

const { EventEmitter } = require('events'); const emitter = new EventEmitter(); const f1 = () => { console.log('f1 被触发'); } const f2 = () => { console.log('f2 被触发'); } emitter.on('some-event', f1); emitter.on('some-event', f2); emitter.emit('some-event'); emitter.removeListener('some-event', f1); emitter.emit('some-event');
Copy after login

在第一个事件触发后,由于f1f2都处于活动状态,这两个函数都将被执行。之后从EventEmitter中删除了f1。当再次发出事件时,将会只执行f2

f1 被触发 f2 被触发 f2 被触发
Copy after login

AnaliasforremoveListener()isoff(). For example, we could have written:

removeListener()的别名是off()。例如可以这样写:

emitter.off('some-event', f1);
Copy after login

removeAllListeners()

该函数用于从EventEmitter的所有事件中删除所有侦听器:

const { EventEmitter } = require('events'); const emitter = new EventEmitter(); const f1 = () => { console.log('f1 被触发'); } const f2 = () => { console.log('f2 被触发'); } emitter.on('some-event', f1); emitter.on('some-event', f2); emitter.emit('some-event'); emitter.removeAllListeners(); emitter.emit('some-event');
Copy after login

第一个emit()会同时触发f1f2,因为它们当时正处于活动状态。删除它们后,emit()函数将发出事件,但没有侦听器对此作出响应:

f1 被触发 f2 被触发
Copy after login

错误处理

如果要在EventEmitter发出错误,必须用error事件名来完成。这是 Node.js 中所有EventEmitter对象的标准配置。这个事件必须还要有一个Error对象。例如可以像这样发出错误事件:

myEventEmitter.emit('error', new Error('出现了一些错误'));
Copy after login

error事件的侦听器都应该有一个带有一个参数的回调,用来捕获Error对象并处理。如果EventEmitter发出了error事件,但是没有订阅者订阅error事件,那么 Node.js 程序将会抛出这个Error。这会导致 Node.js 进程停止运行并退出程序,同时在控制台中显示这个错误的跟踪栈。

例如在CountDown类中,countdownTime参数的值不能小于 2,否则会无法触发end-soon事件。在这种情况下应该发出一个error事件:

class CountDown extends EventEmitter { constructor(countdownTime) { super(); if (countdownTimer < 2) { this.emit('error', new Error('countdownTimer 的值不能小于2')); } this.countdownTime = countdownTime; this.currentTime = 0; } // ........... }
Copy after login

处理这个错误的方式与其他事件相同:

myCountDown.on('error', (err) => { console.error('发生错误:', err); });
Copy after login

始终对error事件进行监听是一种很专业的做法。

使用EventEmitter的原生模块

Node.js 中许多原生模块扩展了EventEmitter类,因此它们本身就是事件发射器。

一个典型的例子是Stream类。官方文档指出:

流可以是可读的、可写的,或两者均可。所有流都是 EventEmitter的实例。

先看一下经典的 Stream 用法:

const fs = require('fs'); const writer = fs.createWriteStream('example.txt'); for (let i = 0; i < 100; i++) { writer.write(`hello, #${i}!\n`); } writer.on('finish', () => { console.log('All writes are now complete.'); }); writer.end('This is the end\n');
Copy after login

但是,在写操作和writer.end()调用之间,我们添加了一个侦听器。Stream在完成后会发出一个finished事件。在发生错误时会发出error事件,把读取流通过管道传输到写入流时会发出pipe事件,从写入流中取消管道传输时,会发出unpipe事件。

另一个类是child_process类及其spawn()方法:

const { spawn } = require('child_process'); const ls = spawn('ls', ['-lh', '/usr']); ls.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); ls.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); ls.on('close', (code) => { console.log(`child process exited with code ${code}`); });
Copy after login

child_process写入标准输出管道时,将会触发stdoutdata事件。当输出流遇到错误时,将从stderr管道发送data事件。

Finally, after the process exits, thecloseevent will be triggered.

Summary

Event-driven architecture enables us to create systems with high cohesion and low coupling. An event represents the result of an action, and one or more listeners can be defined and react to it.

This article takes an in-depth look at theEventEmitterclass and its capabilities. Instantiate it and use it directly, and extend its behavior into a custom object.

Finally, some important functions of this class are introduced.

For more programming-related knowledge, please visit:Programming Courses! !

The above is the detailed content of How to use EventEmitter to handle events in Node.js?. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!