Menu

EventEmitter Node.js : on, emit, once et événements

EventEmitter, du module events de Node, permet à un objet d'annoncer des événements nommés avec emit et à d'autres parties du code d'y réagir avec on ou once. Les listeners s'exécutent de façon synchrone, dans l'ordre où ils ont été ajoutés.

Cette page contient des éditeurs exécutables. Modifiez, exécutez et voyez la sortie instantanément.

EventEmitter est la classe du module events de Node qui permet à un objet d'annoncer des événements nommés. Vous enregistrez un listener avec emitter.on('name', fn) et vous le déclenchez avec emitter.emit('name', ...args) ; chaque listener de ce nom s'exécute, dans l'ordre où il a été ajouté, avec les arguments passés. Les serveurs HTTP, les streams et process sont tous des EventEmitters, donc les mêmes quelques méthodes marchent partout dans Node.

Les deux listeners s'exécutent entre before emit et after emit : emit les appelle de façon synchrone et ne rend la main qu'une fois qu'ils ont fini. Inversez les deux appels à shop.on et les deux lignes du milieu s'inversent aussi.

on, once et off

on garde un listener jusqu'à ce que vous le retiriez, once le retire après le premier appel, et off (identique à removeListener) en retire un. Pour retirer un listener, il faut la même fonction que celle ajoutée, donc donnez-lui un nom au lieu de passer une fonction fléchée directement.

first open ever s'affiche une seule fois, après Ada. Une fois logOpen retiré, plus personne n'écoute, donc le dernier emit renvoie false ; door.removeAllListeners('open') aurait supprimé les deux listeners en un seul appel.

MéthodeRôle
on(name, fn), addListenerAppelle fn à chaque emit
once(name, fn)Appelle fn au prochain emit seulement
prependListener(name, fn)Comme on, mais passe avant les listeners existants
off(name, fn), removeListenerRetire cette fonction
emit(name, ...args)Appelle les listeners, renvoie true s'il y en avait
listenerCount(name)Le nombre de listeners de l'événement

L'événement error

'error' est un cas à part. Si vous l'émettez et que personne n'écoute, emit lève l'erreur, et dans un vrai programme une exception non interceptée fait planter le processus. Ajoutez toujours un listener 'error' aux emitters qui peuvent échouer, comme les streams et les serveurs.

Sans listener, emit lève l'erreur et le catch la récupère ; une fois le listener ajouté, le même emit se contente de l'appeler et le programme continue. Tout autre nom d'événement sans listener est ignoré sans bruit.

Étendre EventEmitter dans votre propre classe

La façon habituelle de l'utiliser est de faire hériter votre classe de EventEmitter, d'appeler super() dans le constructeur et this.emit quand quelque chose se produit. Les appelants s'abonnent ensuite à votre objet comme à n'importe quel objet Node.

Le programme affiche 25%, 50%, 75%, 100% puis photo.png uploaded. Placez up.start() au-dessus des deux lignes up.on et rien ne s'affiche : les événements sont émis avant que quiconque n'écoute.

Le même piège existe quand on émet depuis un constructeur. Les bibliothèques qui doivent émettre tout de suite enveloppent l'appel dans process.nextTick, qui s'exécute après les lignes on de l'appelant (voir la page sur l'event loop).

Attendre un événement avec events.once

events.once(emitter, name) renvoie une promesse qui se résout avec les arguments de l'événement, sous forme de tableau, la prochaine fois qu'il est émis. Un événement ponctuel devient ainsi quelque chose que vous pouvez await, et la promesse est rejetée si l'emitter émet 'error' d'abord.

L'await met le code en pause jusqu'à ce que le timer émette ready, puis le tableau est déstructuré en deux arguments. Pour un événement émis de nombreuses fois, events.on(emitter, name) donne un itérateur asynchrone que vous pouvez parcourir avec for await.

Erreurs fréquentes

Ajouter un listener dans une fonction appelée souvent. Appeler on dans chaque handler de requête empile les listeners et la mémoire. Ajoutez-le une seule fois, ou utilisez once.

Retirer une fonction écrite en ligne. off('data', () => ...) ne retire rien, car cette fonction fléchée est une nouvelle fonction. Gardez une référence à la fonction ajoutée.

S'attendre à ce que les listeners s'exécutent plus tard. emit est synchrone, donc un listener lent retarde le code qui a émis. Marquer le listener async n'aide pas, car son code s'exécute toujours sur le même thread jusqu'au premier await. Découpez le travail lourd en morceaux séparés par des setImmediate, ou déplacez-le dans un worker thread.

Questions fréquentes

EventEmitter est-il asynchrone ?

Non. emit appelle chaque listener immédiatement, l'un après l'autre, et rend la main quand le dernier a fini. Le code d'un listener peut lancer un travail asynchrone, mais le listener lui-même est appelé de façon synchrone.

Que signifie l'avertissement MaxListenersExceededWarning ?

Node prévient quand plus de 10 listeners sont ajoutés pour un même événement, car cela veut souvent dire qu'un listener est ajouté à chaque requête sans jamais être retiré. Retirez les listeners en trop, ou appelez emitter.setMaxListeners(n) si vous en avez vraiment besoin de plus.

Que renvoie emit ?

true si l'événement avait au moins un listener, false si personne n'écoutait. Il ne renvoie pas ce que renvoient les listeners.

Quels objets Node.js sont des EventEmitters ?

La plupart des objets du cœur de Node : les serveurs HTTP, les requêtes et les réponses, les streams, les sockets, process et les interfaces readline. C'est pour cela qu'ils partagent tous on, once et off.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

Commencer