Maison > interface Web > js tutoriel > Comment utiliser EventEmitter pour gérer les événements dans Node.js ?

Comment utiliser EventEmitter pour gérer les événements dans Node.js ?

青灯夜游
Libérer: 2020-11-02 17:46:09
avant
2634 Les gens l'ont consulté

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.

Qu'est-ce qu'un événement ?

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.

Qu'est-ce qu'EventEmitter ? La classe

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

Créer des EventEmitters

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

de l'objet EventEmitter

Commencez par un exemple simple : créez un

qui émet un. événement contenant des informations d'exécution du programme chaque seconde EventEmitter

Importez d'abord la classe

depuis le module events : EventEmitter

const { EventEmitter } = require('events');
Copier après la connexion
Créez ensuite un

 : EventEmitter

const timerEventEmitter = new EventEmitter();
Copier après la connexion
. est très simple de publier un événement avec cet objet :

timerEventEmitter.emit("update");
Copier après la connexion
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);
Copier après la connexion
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} 秒`);
});
Copier après la connexion
updateCré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()

est un rappel qui peut être accepté

on()L'exécution du code affichera : update

从发布者收到的消息:
程序已经运行了 1 秒
从发布者收到的消息:
程序已经运行了 2 秒
从发布者收到的消息:
程序已经运行了 3 秒
...
Copier après la connexion
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} 秒`);
});
Copier après la connexion
L'exécution de ce code affichera :

从发布者收到的消息:
程序已经运行了 1 秒
Copier après la connexion

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é :

: Déclenche une fois par seconde

: Déclenche à la fin du compte à rebours
  • 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;
};
Copier après la connexion

这个函数启动了一个每秒钟发出一次 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秒后结束');
});
Copier après la connexion

这段代码将会输出:

程序已经运行了 1 秒
程序已经运行了 2 秒
程序已经运行了 3 秒
计时将在2秒后结束
程序已经运行了 4 秒
程序已经运行了 5 秒
计时结束
Copier après la connexion
Copier après la connexion

扩展 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);
    }
}
Copier après la connexion

可以在类的内部直接使用 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();
Copier après la connexion

运行程序会输出:

程序已经运行了 1 秒
程序已经运行了 2 秒
程序已经运行了 3 秒
计时将在2秒后结束
程序已经运行了 4 秒
程序已经运行了 5 秒
计时结束
Copier après la connexion
Copier après la connexion

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

myCountDown.on('end-soon', () => {
    console.log('计时将在2秒后结束');
});
Copier après la connexion

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

myCountDown.addListener('end-soon', () => {
    console.log('计时将在2秒后结束');
});
Copier après la connexion

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());
Copier après la connexion

运行这段代码会输出:

[ 'update', 'end', 'end-soon' ]
Copier après la connexion

如果要订阅另一个事件,例如 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');
Copier après la connexion

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

f1 被触发
f2 被触发
f2 被触发
Copier après la connexion

An alias for removeListener() is off(). For example, we could have written:

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

emitter.off('some-event', f1);
Copier après la connexion

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');
Copier après la connexion

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

f1 被触发
f2 被触发
Copier après la connexion

错误处理

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

myEventEmitter.emit('error', new Error('出现了一些错误'));
Copier après la connexion

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(&#39;error&#39;, new Error(&#39;countdownTimer 的值不能小于2&#39;));
        }

        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    // ...........
}
Copier après la connexion

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

myCountDown.on(&#39;error&#39;, (err) => {
    console.error('发生错误:', err);
});
Copier après la connexion

始终对 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(&#39;finish&#39;, () => {
  console.log('All writes are now complete.');
});

writer.end('This is the end\n');
Copier après la connexion

但是,在写操作和 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}`);
});
Copier après la connexion

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

Enfin, une fois le processus terminé, l'événement close sera déclenché.

Résumé

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!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal