Abschnitt 7 · Lektion 2
Eigene Ereignisse
Bisher war der EventEmitter ein Objekt für sich. Der übliche Weg ist ein anderer: Du erbst von
ihm, und damit kann dein eigenes Objekt melden, ohne dass jemand ein zweites danebenstellen muss.
import { EventEmitter } from "node:events";
class Warteschlange extends EventEmitter {
constructor() {
super();
this.auftraege = [];
}
hinzufuegen(name) {
this.auftraege.push(name);
this.emit("aufgenommen", name, this.auftraege.length);
}
abarbeiten() {
while (this.auftraege.length > 0) {
this.emit("erledigt", this.auftraege.shift());
}
this.emit("leer");
}
}
const schlange = new Warteschlange();
schlange.on("aufgenommen", (name, offen) => console.log(`aufgenommen: ${name} (${offen} offen)`));
schlange.on("erledigt", (name) => console.log(`erledigt: ${name}`));
schlange.on("leer", () => console.log("nichts mehr zu tun"));
schlange.hinzufuegen("Rechnung");
schlange.hinzufuegen("Newsletter");
schlange.abarbeiten(); extends EventEmitter und ein super() im Konstruktor, mehr ist es nicht. Ab da hat deine Klasse
on, once, emit und off, so wie ein Server oder ein Stream sie auch hat. Von außen sieht dein
Objekt damit aus wie ein eingebautes.
Genau so ist die halbe Standardbibliothek gebaut. Wenn du später server.on("request", ...)
schreibst, ist das dieselbe Mechanik.
Wie ein Ereignisname heißen sollte
Ein guter Ereignisname beschreibt, was passiert ist, nicht was der Zuhörer tun soll.
auftrag-fertig ist gut. sende-bestaetigungsmail ist schlecht, obwohl an dieser Stelle vielleicht
genau das passieren soll. Der Unterschied ist nicht Geschmack, sondern die Frage, wer wen kennt.
Bei auftrag-fertig weiß die Warteschlange nichts über Mails. Kommt später ein zweiter Zuhörer
dazu, der eine Statistik führt, ändert sich an ihr nichts. Bei sende-bestaetigungsmail hat sie
bereits entschieden, was zu tun ist, und der zweite Zuhörer hängt an einem Namen, der nicht zu ihm
passt.
Die Faustregel: Der Name gehört zum Melder, nicht zum Zuhörer. Er ist eine Nachricht über die Vergangenheit.
Die Daten gehören ins emit
Es ist verlockend, das Ergebnis an eine Eigenschaft zu hängen und die Zuhörer dort nachschauen zu lassen. Solange alle Zuhörer sofort laufen, geht das auch gut. Sobald einer wartet, nicht mehr.
import { EventEmitter } from "node:events";
class Melder extends EventEmitter {
melden(text) {
this.letzteMeldung = text;
this.emit("meldung");
}
}
const melder = new Melder();
melder.on("meldung", () => console.log(`sofort: ${melder.letzteMeldung}`));
melder.on("meldung", async () => {
await new Promise((fertig) => setTimeout(fertig, 10));
console.log(`spaeter: ${melder.letzteMeldung}`);
});
melder.melden("Erste Meldung");
melder.melden("Zweite Meldung"); Der zweite Zuhörer wartet zehn Millisekunden, und in dieser Zeit ist die zweite Meldung längst durch. Er liest also zweimal denselben Wert, und die erste Meldung ist für ihn nie passiert.
Mit this.emit("meldung", text) und einem Parameter im Zuhörer gibt es dieses Problem nicht: Jeder
Aufruf bekommt seine eigenen Argumente, und die bleiben auch dann richtig, wenn der Zuhörer sich
Zeit lässt.
Wo Ereignisse aufhören
Bleibt eine Grenze, und die ist wichtig.
import { EventEmitter } from "node:events";
const rechner = new EventEmitter();
rechner.on("rechne", (a, b) => a + b);
const rueckgabe = rechner.emit("rechne", 2, 3);
console.log(`emit gibt zurueck: ${rueckgabe}`);
console.log(`emit ohne Zuhoerer: ${rechner.emit("gibtsnicht")}`); emit gibt dir keinen Wert aus dem Zuhörer zurück, sondern nur ein true oder false dafür, ob
überhaupt jemand zugehört hat. Was die Zuhörer zurückgeben, wird verworfen. Bei einer
async-Funktion heißt das: Das Promise wird weggeworfen, und niemand wartet darauf.
Ereignisse sind für „ist passiert”, nicht für „gib mir”. Sobald du auf ein Ergebnis warten
willst, ist ein Promise die richtige Antwort und nicht ein Ereignis. Eine Methode, die
async abhaken(name) heißt und ein Ergebnis liefert, ist völlig in Ordnung, und sie kann daneben
trotzdem melden, dass etwas passiert ist.
In der Praxis stehen die beiden deshalb oft nebeneinander: Der Aufrufer bekommt sein Ergebnis als Promise, und wer sonst noch mitbekommen will, was läuft, hängt sich an ein Ereignis.
Zum Mitnehmen
Ein Ereignisname sagt, was passiert ist, nicht was jemand tun soll. Und die Daten gehören ins emit, nicht an eine Eigenschaft daneben.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.