Maison > Article > interface Web > Comment utiliser EventEmitter pour gérer les événements dans Node.js ?
Dans ce tutoriel, nous découvrons la classe EvenEmitter
native de Node.js. Après avoir appris, vous comprendrez les événements, comment utiliser EvenEmitter
et comment utiliser les événements dans vos programmes. De plus, vous apprendrez également comment la classe EventEmitter
s'étend à partir d'autres modules locaux et comprendrez les principes qui la sous-tendent à travers quelques exemples.
Tutoriel recommandé : tutoriel node js
En bref, cet article couvre tout sur la classe EventEmitter
.
L'architecture basée sur les événements est très courante aujourd'hui. Les programmes basés sur les événements peuvent générer, détecter et répondre à divers événements.
La partie centrale de Node.js est pilotée par les événements, et de nombreux modules tels que le système de fichiers (fs
) et stream
sont eux-mêmes écrits en utilisant EventEmitter
.
Dans la programmation événementielle, un événement est le résultat d'une ou plusieurs actions, qui peuvent être une opération d'un utilisateur ou la sortie de synchronisation d'un capteur, etc.
Nous pouvons considérer les programmes événementiels comme un modèle de publication-abonnement, dans lequel les éditeurs déclenchent des événements et les abonnés écoutent les événements et prennent les mesures appropriées.
Par exemple, imaginez qu'il existe un serveur sur lequel les utilisateurs peuvent télécharger des images. En programmation événementielle, une action telle que télécharger une image émettra un événement, qui aura également 1 à n abonnés afin d'en profiter.
Une fois l'événement de téléchargement déclenché, un abonné peut le faire savoir et réagir à l'administrateur du site en envoyant un e-mail à l'administrateur du site ; un autre abonné peut collecter des informations sur l'action et les enregistrer dans la base de données.
Ces événements sont généralement indépendants les uns des autres, bien qu'ils puissent également être dépendants les uns des autres.
EventEmitter
est une classe intégrée de Node.js, située dans le module events
. Selon la description dans la documentation :
La plupart des API principales de Node.js sont implémentées sur la base d'une architecture événementielle asynchrone idiomatique, dans laquelle certains types d'objets (appelés " émetteur ») émettent des événements nommés qui provoquer des appels à des Function
objets ("auditeurs")"
Cette classe peut à certains égards être décrite comme une implémentation d'un assistant pour le modèle de publication-abonnement, car elle peut aider les émetteurs d'événements (éditeurs) à publier des événements (messages) aux auditeurs (abonnés) de manière simple
Cela dit, faisons-le d'abord. Créer un EventEmitter
est plus pratique, soit en créant une instance. de la classe elle-même ou en implémentant une classe personnalisée, puis en créant un objet
qui émet un. événement contenant des informations d'exécution du programme chaque seconde EventEmitter
depuis le module events
: EventEmitter
const { EventEmitter } = require('events');Créez ensuite un
: EventEmitter
const timerEventEmitter = new EventEmitter();. est très simple de publier un événement avec cet objet :
timerEventEmitter.emit("update");Le nom de l'événement a été précisé précédemment et il est publié en tant qu'événement. Le programme n'a pas de réponse car aucun auditeur n'a encore réagi à cet événement
Laissez d'abord cet événement se répéter toutes les secondes. Utilisez la méthode
pour créer une minuterie et publiez-la toutes les secondes. Événement :let currentTime = 0; // 每秒触发一次 update 事件 setInterval(() => { currentTime++; timerEventEmitter.emit('update', currentTime); }, 1000);
setInterval()
update
est utilisée pour accepter le nom et les paramètres de l'événement. Passez comme nom de l'événement et comme heure depuis le démarrage du programme. La méthode 🎜> déclenche l'émetteur, qui transmet l'événement avec les informations que nous avons fournies. Après avoir préparé l'émetteur d'événement, abonnez-vous à l'écouteur d'événement pour. it : EventEmitter
timerEventEmitter.on('update', (time) => { console.log('从发布者收到的消息:'); console.log(`程序已经运行了 ${time} 秒`); });
update
Créez l'écouteur via la méthode currentTime
et transmettez-le. Le nom de l'événement pour spécifier à quel événement vous souhaitez attacher l'écouteur. Sur l'événement , exécutez une méthode qui enregistre le. time. Le deuxième paramètre de la fonction emit()
on()
L'exécution du code affichera : update
从发布者收到的消息: 程序已经运行了 1 秒 从发布者收到的消息: 程序已经运行了 2 秒 从发布者收到的消息: 程序已经运行了 3 秒 ...Si vous avez seulement besoin d'effectuer. certaines opérations lorsque l'événement est déclenché pour la première fois, vous pouvez également utiliser la méthode
pour vous abonner : on()
timerEventEmitter.once('update', (time) => { console.log('从发布者收到的消息:'); console.log(`程序已经运行了 ${time} 秒`); });L'exécution de ce code affichera :
从发布者收到的消息: 程序已经运行了 1 秒
EventEmitteronce()
Avec plusieurs auditeurs
Voici un autre programme émetteur d'événements, il y a trois auditeurs, le premier auditeur met à jour l'heure toutes les secondes, le deuxième auditeur se déclenche lorsque le timer est sur le point de se terminer, et le dernier. se déclenche lorsque le timer est terminé :
update
: Déclenchez 2 secondes avant la fin du compte à rebours end
d'abord Écrivez une fonction qui crée cet émetteur d'événement : 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; };
这个函数启动了一个每秒钟发出一次 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秒后结束'); });
这段代码将会输出:
程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束
接下来通过扩展 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); } }
可以在类的内部直接使用 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();
运行程序会输出:
程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束
on()
函数的别名是 addListener()
。看一下 end-soon
事件监听器:
myCountDown.on('end-soon', () => { console.log('计时将在2秒后结束'); });
也可以用 addListener()
来完成相同的操作,例如:
myCountDown.addListener('end-soon', () => { console.log('计时将在2秒后结束'); });
此函数将以数组形式返回所有活动的侦听器名称:
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());
运行这段代码会输出:
[ 'update', 'end', 'end-soon' ]
如果要订阅另一个事件,例如 myCount.on('some-event', ...)
,则新事件也会添加到数组中。
这个方法不会返回已发布的事件,而是返回订阅的事件的列表。
这个函数可以从 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');
在第一个事件触发后,由于 f1
和 f2
都处于活动状态,这两个函数都将被执行。之后从 EventEmitter
中删除了 f1
。当再次发出事件时,将会只执行 f2
:
f1 被触发 f2 被触发 f2 被触发
An alias for removeListener()
is off()
. For example, we could have written:
removeListener()
的别名是 off()
。例如可以这样写:
emitter.off('some-event', f1);
该函数用于从 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');
第一个 emit()
会同时触发 f1
和 f2
,因为它们当时正处于活动状态。删除它们后,emit()
函数将发出事件,但没有侦听器对此作出响应:
f1 被触发 f2 被触发
如果要在 EventEmitter
发出错误,必须用 error
事件名来完成。这是 Node.js 中所有 EventEmitter
对象的标准配置。这个事件必须还要有一个 Error
对象。例如可以像这样发出错误事件:
myEventEmitter.emit('error', new Error('出现了一些错误'));
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; } // ........... }
处理这个错误的方式与其他事件相同:
myCountDown.on('error', (err) => { console.error('发生错误:', err); });
始终对 error
事件进行监听是一种很专业的做法。
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');
但是,在写操作和 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}`); });
当 child_process
写入标准输出管道时,将会触发 stdout
的 data
事件。当输出流遇到错误时,将从 stderr
管道发送 data
事件。
Enfin, une fois le processus terminé, l'événement close
sera déclenché.
L'architecture événementielle nous permet de créer des systèmes avec une cohésion élevée et un faible couplage. Un événement représente le résultat d'une action, et un ou plusieurs auditeurs peuvent être définis et y réagir.
Cet article examine en profondeur la classe EventEmitter
et ses fonctionnalités. Instanciez-le et utilisez-le directement, et étendez son comportement dans un objet personnalisé.
Enfin, quelques fonctions importantes de cette classe sont introduites.
Pour plus de connaissances liées à la programmation, veuillez visiter : Cours de programmation ! !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!