EventEmitter ist die Klasse im events-Modul von Node, mit der ein Objekt benannte Events ankündigen kann. Du registrierst einen Listener mit emitter.on('name', fn) und löst ihn mit emitter.emit('name', ...args) aus; jeder Listener für diesen Namen läuft, in der Reihenfolge, in der er hinzugefügt wurde, mit den Argumenten, die du übergibst. HTTP-Server, Streams und process sind alle EventEmitter, deshalb funktionieren dieselben paar Methoden überall in Node.
Beide Listener laufen zwischen before emit und after emit: emit ruft sie synchron auf und kehrt erst zurück, wenn sie fertig sind. Vertausch die beiden shop.on-Aufrufe, und die beiden mittleren Zeilen tauschen ebenfalls die Plätze.
on, once und off
on behält einen Listener, bis du ihn entfernst, once entfernt ihn nach dem ersten Aufruf, und off (dasselbe wie removeListener) entfernt einen. Zum Entfernen brauchst du genau die Funktion, die du hinzugefügt hast, also gib ihr einen Namen, statt eine Arrow Function direkt zu übergeben.
first open ever erscheint einmal, nach Ada. Sobald logOpen entfernt ist, hört niemand mehr zu, deshalb gibt das letzte emit false zurück; door.removeAllListeners('open') hätte beide Listener mit einem Aufruf entfernt.
| Methode | Macht |
|---|---|
on(name, fn), addListener | Ruft fn bei jedem emit auf |
once(name, fn) | Ruft fn nur beim nächsten emit auf |
prependListener(name, fn) | Wie on, läuft aber vor den vorhandenen Listenern |
off(name, fn), removeListener | Entfernt diese Funktion |
emit(name, ...args) | Ruft die Listener auf, gibt true zurück, wenn es welche gab |
listenerCount(name) | Wie viele Listener das Event hat |
Das error-Event
'error' ist besonders. Wenn du es auslöst und niemand zuhört, wirft emit den Fehler, und in einem echten Programm bringt ein nicht abgefangener Fehler den Prozess zum Absturz. Häng an Emitter, die fehlschlagen können, etwa Streams und Server, immer einen 'error'-Listener.
Ohne Listener wirft emit, und der catch fängt den Fehler; mit dem Listener ruft dasselbe emit ihn einfach auf, und das Programm läuft weiter. Jeder andere Event-Name ohne Listener wird stillschweigend ignoriert.
EventEmitter in einer eigenen Klasse erweitern
Üblich ist, dass deine Klasse EventEmitter erweitert, im Konstruktor super() aufruft und this.emit, wenn etwas passiert. Aufrufer abonnieren dein Objekt dann wie jedes Node-Objekt.
Es gibt 25%, 50%, 75%, 100% und dann photo.png uploaded aus. Schieb up.start() über die beiden up.on-Zeilen, und nichts erscheint: Die Events feuern, bevor jemand zuhört.
Dieselbe Falle gibt es beim Auslösen von Events im Konstruktor. Bibliotheken, die sofort ein Event auslösen müssen, packen den Aufruf in process.nextTick, das nach den on-Zeilen des Aufrufers läuft (siehe die Seite zur Event Loop).
Mit events.once auf ein Event warten
events.once(emitter, name) gibt ein Promise zurück, das beim nächsten Auslösen mit den Argumenten des Events als Array aufgelöst wird. So wird aus einem einmaligen Event etwas, auf das du mit await warten kannst, und es wird abgelehnt, wenn der Emitter vorher 'error' auslöst.
Das await pausiert, bis der Timer ready auslöst, dann wird das Array in die beiden Argumente zerlegt. Für ein Event, das oft feuert, liefert events.on(emitter, name) einen Async Iterator, den du mit for await durchlaufen kannst.
Häufige Fehler
Einen Listener in einer Funktion hinzufügen, die oft läuft. on in jedem Request-Handler aufzurufen, stapelt Listener und Speicher. Füg ihn einmal hinzu oder nimm once.
Eine Inline-Funktion entfernen. off('data', () => ...) entfernt nichts, weil diese Arrow Function eine neue ist. Behalte eine Referenz auf die Funktion, die du hinzugefügt hast.
Erwarten, dass Listener später laufen. emit ist synchron, ein langsamer Listener hält also den Code auf, der das Event ausgelöst hat. Den Listener als async zu markieren hilft nicht, denn sein Code läuft bis zum ersten await trotzdem im selben Thread. Teil schwere Arbeit in Stücke mit setImmediate dazwischen auf, oder verschieb sie in einen Worker-Thread.
Häufig gestellte Fragen
Ist EventEmitter asynchron?
Nein. emit ruft jeden Listener sofort auf, einen nach dem anderen, und kehrt zurück, wenn der letzte fertig ist. Code in einem Listener kann asynchrone Arbeit starten, aber der Listener selbst wird synchron aufgerufen.
Was bedeutet die MaxListenersExceededWarning?
Node warnt, wenn mehr als 10 Listener für ein Event hinzugefügt werden, weil das oft heißt, dass bei jeder Anfrage ein Listener dazukommt und nie entfernt wird. Entferne die überzähligen Listener oder ruf emitter.setMaxListeners(n) auf, wenn du wirklich mehr brauchst.
Was gibt emit zurück?
true, wenn das Event mindestens einen Listener hatte, false, wenn niemand zugehört hat. Es gibt nicht zurück, was die Listener zurückgeben.
Welche Node.js-Objekte sind EventEmitter?
Die meisten Kernobjekte von Node: HTTP-Server, Requests und Responses, Streams, Sockets, process und readline-Interfaces. Deshalb haben sie alle on, once und off.