In Node geben viele Objekte Ereignisse aus. Beispielsweise sendet ein TCP-Server jedes Mal ein „Connect“-Ereignis aus, wenn ein Client eine Verbindung anfordert. Beispielsweise sendet das Dateisystem jedes Mal ein „Data“-Ereignis aus, wenn ein ganzer Datenblock gelesen wird. Diese Objekte werden in Node als Ereignisemitter bezeichnet. Ereignisemitter ermöglichen es Programmierern, die Ereignisse zu abonnieren, an denen sie interessiert sind, und Rückruffunktionen an die relevanten Ereignisse zu binden, sodass die Rückruffunktion jedes Mal aufgerufen wird, wenn der Ereignisemitter ein Ereignis ausgibt. Das Publish/Subscribe-Modell ist dem herkömmlichen GUI-Modell sehr ähnlich. Wenn beispielsweise auf eine Schaltfläche geklickt wird, erhält das Programm die entsprechende Benachrichtigung. In diesem Modus kann das Serverprogramm reagieren, wenn bestimmte Ereignisse eintreten, z. B. wenn ein Client eine Verbindung herstellt, Daten auf dem Socket verfügbar sind oder wenn die Datei geschlossen wird.
Sie können auch Ihren eigenen Event-Emitter erstellen. Tatsächlich stellt Node eine EventEmitter-Pseudoklasse bereit, die als Basisklasse zum Erstellen Ihres eigenen Event-Emitters verwendet werden kann.
Das Rückrufmuster verstehen
Asynchrone Programmierung verwendet keine Funktionsrückgabewerte, um das Ende von Funktionsaufrufen anzuzeigen, sondern übernimmt den Nachfolgerbereitstellungsstil.
„Continuation-Passing-Stil“ (CPS: Continuation-Passing-Stil) ist ein Programmierstil, bei dem die Flusskontrolle explizit an die nächste Operation übergeben wird...
Die Funktion im CPS-Stil akzeptiert eine Funktion als zusätzlichen Parameter. Diese Funktion wird verwendet, um den nächsten vom Programm gesteuerten Prozess explizit anzugeben. Wenn die CPS-Funktion ihren „Rückgabewert“ berechnet, ruft sie die Funktion auf nächsten Schritt des Programms. Funktion eines Prozesses und verwendet den „Rückgabewert“ der CPS-Funktion als Parameter.
Aus Wikipedia – http://en.wikipedia.org/wiki/Continuation-passing_style
Bei diesem Programmierstil ruft jede Funktion nach der Ausführung eine Rückruffunktion auf, damit das Programm weiterlaufen kann. Später werden Sie verstehen, dass JavaScript für diesen Programmierstil sehr gut geeignet ist. Hier ist ein Beispiel für das Laden einer Datei in den Speicher unter Knoten:
fs.readFile('/etc/passwd', function(err, fileContent) {
if (err) {
wirf err;
}
console.log('file content', fileContent.toString());
});
In diesem Beispiel übergeben Sie eine anonyme Inline-Funktion als zweiten Parameter von fs.readFile. Tatsächlich handelt es sich hierbei um eine Programmierung mit CPS, da Sie den nachfolgenden Prozess der Programmausführung an die Callback-Funktion übergeben.
Wie Sie sehen können, ist der erste Parameter der Callback-Funktion ein Fehlerobjekt. Wenn im Programm ein Fehler auftritt, ist dieser Parameter eine Instanz der Error-Klasse. Dies ist ein häufiges Muster bei der CPS-Programmierung in Node.
Das Ereignis-Emitter-Muster verstehen
Im Standard-Callback-Modus wird eine Funktion als Parameter an die auszuführende Funktion übergeben. Dieser Modus eignet sich gut für Szenarien, in denen der Client nach Abschluss der Funktion benachrichtigt werden muss. Dieses Modell ist jedoch nicht geeignet, wenn während der Ausführung der Funktion mehrere Ereignisse auftreten oder wenn das Ereignis mehrmals wiederholt auftritt. Sie möchten beispielsweise jedes Mal benachrichtigt werden, wenn der Socket verfügbare Daten empfängt. In diesem Fall werden Sie feststellen, dass der Standard-Callback-Modus nicht sehr einfach zu verwenden ist eine Reihe von Standardschnittstellen zur klaren Trennung von Ereignisgeneratoren und Ereignis-Listenern.
Bei Verwendung des Ereignisgeneratormusters sind zwei oder mehr Objekte beteiligt – ein Ereignisemitter und ein oder mehrere Ereignislistener.
Ereignisemitter ist, wie der Name schon sagt, ein Objekt, das Ereignisse generieren kann. Der Ereignis-Listener ist der an den Ereignis-Emitter gebundene Code, der auf bestimmte Ereignistypen wartet, wie im folgenden Beispiel:
Response.on("data", function(data) {
console.log("einige Daten aus der Antwort", Daten);
});
Response.on("end", function() {
console.log("Antwort beendet");
});
});
req.end();
Dieser Code demonstriert die beiden Schritte, die zum Erstellen einer HTTP-Anfrage für den Zugriff auf einen Remote-HTTP-Server mithilfe der http.request-API von Node erforderlich sind (siehe spätere Kapitel). Die erste Zeile verwendet den „Continuation-Passing-Stil“ (CPS: Continuation-Passing-Stil) und übergibt eine Inline-Funktion, die aufgerufen wird, wenn die HTTP-Antwort erfolgt. Die HTTP-Anforderungs-API verwendet hier CPS, da das Programm nach der Ausführung der http.request-Funktion weiterhin nachfolgende Vorgänge ausführen muss.
Wenn http.request ausgeführt wird, wird die anonyme Rückruffunktion aufgerufen und dann das HTTP-Antwortobjekt als Parameter übergeben. Laut Node-Dokumentation ist dies möglich Daten ausgeben, enden bei Bei vielen Ereignissen werden die von Ihnen registrierten Rückruffunktionen jedes Mal aufgerufen, wenn das Ereignis auftritt.
Als Faustregel gilt: Verwenden Sie das CPS-Muster, wenn Sie Ausführungsrechte nach Abschluss des angeforderten Vorgangs wiedererlangen müssen, und verwenden Sie das Event Emitter-Muster, wenn ein Ereignis mehrmals auftreten kann.
Ereignistypen verstehen
Emittierte Ereignisse haben einen Typ, der durch eine Zeichenfolge dargestellt wird. Das vorherige Beispiel umfasst zwei Ereignistypen: „data“ und „end“. Wörter, die keine Nullzeichen enthalten.
Sie können keinen Code verwenden, um abzuleiten, welche Arten von Ereignissen ein Ereignis-Emitter erzeugen kann, da die Ereignis-Emitter-API keinen Selbstprüfungsmechanismus hat. Daher sollte die von Ihnen verwendete API über eine Dokumentation verfügen, die zeigt, welche Arten von Ereignissen sie ausgeben kann.
Sobald ein Ereignis auftritt, ruft der Ereignisemitter den Listener auf, der sich auf das Ereignis bezieht, und übergibt die relevanten Daten als Parameter an den Listener. Im vorherigen http.request-Beispiel akzeptiert die Ereignisrückruffunktion „data“ ein Datenobjekt als ersten und einzigen Parameter, während „end“ keine Daten akzeptiert. Diese Parameter werden auch vom API-Autor als Teil der API festgelegt Subjektiv definiert, werden die Parametersignaturen dieser Rückruffunktionen auch in der API-Dokumentation jedes Ereignisemitters beschrieben.
Obwohl der Event-Emitter eine Schnittstelle ist, die alle Arten von Events bedient, ist das „error“-Event eine spezielle Implementierung in Node. Die meisten Ereignis-Emitter in Node generieren ein „Fehler“-Ereignis, wenn im Programm ein Fehler auftritt. Wenn das Programm nicht auf das „Fehler“-Ereignis eines Ereignis-Emitters hört, wird es vom Ereignis-Emitter bemerkt und ausgelöst, wenn ein Fehler auftritt . Eine nicht erfasste Ausnahme.
Sie können den folgenden Code in Node PERL ausführen, um den Effekt zu testen. Er simuliert einen Ereignisemitter, der zwei Ereignisse generieren kann:
em.emit('event1');
em.emit('error', new Error('My error'));
Sie sehen die folgende Ausgabe:
undefiniert
> em.emit('event1');
falsch
> em.emit('error', new Error('My error'));
Fehler: Mein Fehler
bei repl:1:18
bei REPLServer.eval (repl.js:80:21)
bei repl.js:190:20
bei REPLServer.eval (repl.js:87:5)
bei Interface.
bei Interface.emit (events.js:67:17)
bei Interface._onLine (readline.js:162:10)
bei Interface._line (readline.js:426:8)
bei Interface._ttyWrite (readline.js:603:14)
bei ReadStream.
>
In Zeile 2 des Codes wird zufällig ein Ereignis namens „event1“ ausgegeben, das keine Auswirkung hat. Wenn jedoch das Ereignis „error“ ausgegeben wird, wird der Fehler auf den Stapel geworfen. Wenn das Programm nicht in einer PERL-Befehlszeilenumgebung ausgeführt wird, stürzt das Programm aufgrund nicht abgefangener Ausnahmen ab.
Verwendung der Event Emitter API
Jedes Objekt, das das Ereignis-Emitter-Muster implementiert (z. B. TCP-Socket, HTTP-Anfrage usw.), implementiert den folgenden Satz von Methoden:
Wir stellen sie im Folgenden ausführlich vor.
Verwenden Sie .addListener() oder .on(), um die Rückruffunktion zu binden
Durch Angabe des Ereignistyps und der Rückruffunktion können Sie Vorgänge registrieren, die beim Eintreten des Ereignisses ausgeführt werden sollen. Wenn beispielsweise ein Datenblock verfügbar ist, wenn die Datei den Datenstrom liest, wird ein „Daten“-Ereignis ausgegeben. Der folgende Code zeigt, wie das Programm Ihnen mitteilen kann, dass ein Datenereignis aufgetreten ist, indem eine Rückruffunktion übergeben wird .
console.log("Daten aus Dateilesestream erhalten: %j", Daten);
}
readStream.addListener(“data”,empfangenData);
Sie können auch .on verwenden, was nur eine Abkürzung für .addListener ist. Der folgende Code ist derselbe wie oben:
console.log("Daten aus Dateilesestream erhalten: %j", Daten);
}
readStream.on(“data”, takeData);
Der vorherige Code verwendet eine vordefinierte benannte Funktion als Rückruffunktion. Sie können auch eine anonyme Inline-Funktion verwenden, um den Code zu vereinfachen:
console.log("Daten aus Dateilesestream erhalten: %j", Daten);
});
Mehrere Ereignis-Listener binden
Das Ereignis-Emitter-Muster ermöglicht es mehreren Ereignis-Listenern, denselben Ereignistyp desselben Ereignis-Emitters abzuhören, wie zum Beispiel:
Ich habe hier auch einige Daten.
Der Ereignisemitter ist dafür verantwortlich, alle an den angegebenen Ereignistyp gebundenen Listener in der Reihenfolge aufzurufen, in der die Listener registriert wurden, also:
1. Wenn ein Ereignis auftritt, wird der Ereignis-Listener möglicherweise nicht sofort aufgerufen. Möglicherweise werden davor andere Ereignis-Listener aufgerufen.
2. Es handelt sich um ein abnormales Verhalten, wenn Ausnahmen auf den Stapel geworfen werden. Wenn ein Ereignis-Listener beim Ausgeben eines Ereignisses eine Ausnahme auslöst, kann dies zu einem Ereignis führen Zuhörer werden niemals aufgerufen. In diesem Fall fängt der Ereignisemitter die Ausnahme ab und behandelt sie möglicherweise.
Sehen Sie sich dieses Beispiel an:
wirft einen neuen Fehler aus („Es ist ein Fehler aufgetreten“);
});
readStream.on("data", function(data) {
console.log('Ich habe hier auch einige Daten.');
});
Da der erste Listener eine Ausnahme ausgelöst hat, wird der zweite Listener nicht aufgerufen.
Entfernen Sie einen Ereignis-Listener von einem Ereignis-Emitter mit .removeListener()
Wenn Sie sich nicht mehr für ein Ereignis eines Objekts interessieren, können Sie den registrierten Ereignis-Listener abbrechen, indem Sie den Ereignistyp und die Rückruffunktion wie folgt angeben:
console.log("Daten aus Dateilesestream erhalten: %j", Daten);
}
readStream.on("data", retainData);
// ...
readStream.removeListener("data",empfangenData);
In diesem Beispiel entfernt die letzte Zeile einen Ereignis-Listener aus dem Ereignis-Emitter-Objekt, der jederzeit in der Zukunft aufgerufen werden kann.
Um den Listener zu löschen, müssen Sie die Callback-Funktion benennen, da beim Hinzufügen und Entfernen der Name der Callback-Funktion benötigt wird.
Verwenden Sie .once(), um die Rückruffunktion höchstens einmal auszuführen
Wenn Sie ein Ereignis überwachen möchten, das höchstens einmal ausgeführt werden kann, oder nur daran interessiert sind, wann ein Ereignis zum ersten Mal auftritt, können Sie die Funktion .once() verwenden:
console.log("Daten aus Dateilesestream erhalten: %j", Daten);
}
readStream.once("data",empfangenData);
Im obigen Code wird die Funktion „receiveData“ nur einmal aufgerufen. Wenn das readStream-Objekt das Datenereignis ausgibt, wird die Rückruffunktion „receiveData“ nur einmal ausgelöst.
Es handelt sich eigentlich nur um eine praktische Methode, da sie sehr einfach zu implementieren ist, etwa so:
EventEmitter.prototype.once = function(type, callback) {
var that = this;
this.on(type, function listener() {
that.removeListener(type, listener);
callback.apply(that, arguments);
});
};
Im obigen Code definieren Sie die Funktion EventEmitter.prototype.once neu und definieren auch die Once-Funktion jedes Objekts neu, das von EventEmitter erbt. Der Code verwendet einfach die Methode .on(). Sobald das Ereignis empfangen wurde, verwendet er .removeEventListener(), um die Registrierung der Rückruffunktion abzubrechen und die ursprüngliche Rückruffunktion aufzurufen.
Hinweis: Im vorherigen Code wird die Methode function.apply() verwendet, die ein Objekt akzeptiert und es als enthaltene Variable sowie als Parameterarray verwendet. Im vorherigen Beispiel wird das unveränderte Parameterarray über den Ereignisemitter transparent an die Rückruffunktion übergeben.
Entfernen Sie alle Ereignis-Listener vom Ereignis-Emitter mit .removeAllListeners()
Sie können alle für einen bestimmten Ereignistyp registrierten Listener wie folgt aus einem Ereignisemitter entfernen:
Zum Beispiel können Sie alle Prozessunterbrechungssignal-Listener wie folgt abbrechen:
Hinweis: Als Faustregel wird empfohlen, diese Funktion nur zu verwenden, wenn Sie genau wissen, was gelöscht wird. Andernfalls sollten Sie andere Teile der Anwendung die Ereignis-Listener-Sammlung löschen lassen oder diese löschen lassen Teile der Anwendung übernehmen die Verantwortung für sich selbst. Dennoch ist diese Funktion in einigen seltenen Fällen immer noch sehr nützlich, beispielsweise wenn Sie sich darauf vorbereiten, einen Ereignis-Emitter ordnungsgemäß herunterzufahren oder den gesamten Prozess herunterzufahren.
Ereignis-Emitter erstellen
Ereignis-Emitter sind eine großartige Möglichkeit, Programmierschnittstellen vielseitiger zu gestalten. In einem allgemeinen und leicht verständlichen Programmiermuster ruft der Client direkt verschiedene Funktionen auf, während der Client im Ereignis-Emitter-Muster an verschiedene Ereignisse gebunden ist. Dadurch wird Ihr Programm flexibler. (Anmerkung des Übersetzers: Dieser Satz ist nicht sehr sicher, deshalb habe ich den Originaltext gepostet: Der Ereignisemitter bietet eine großartige Möglichkeit, eine Programmierschnittstelle allgemeiner zu gestalten. Wenn Sie ein allgemein verständliches Muster verwenden, binden Clients an Ereignisse, anstatt Funktionen aufzurufen. Machen Sie Ihr Programm flexibler.)
Darüber hinaus können Sie durch die Verwendung von Ereignisemittern auch viele Funktionen nutzen, z. B. die Bindung mehrerer unabhängiger Listener an dasselbe Ereignis.
Erbt vom Node-Ereignissemitter
Wenn Sie sich für das Event-Emitter-Muster von Node interessieren und planen, es in Ihrer eigenen Anwendung zu verwenden, können Sie eine Pseudoklasse erstellen, indem Sie EventEmitter erben:
var EventEmitter = require('events').EventEmitter;
// Dies ist der Konstruktor von MyClass:
var MyClass = function() {
}
util.inherits(MyClass, EventEmitter);
Hinweis: util.inherits richtet die Prototypenkette von MyClass ein, damit Ihre MyClass-Instanz die Prototypenmethode von EventEmitter verwenden kann.
Eröffnungsveranstaltung
Durch die Vererbung von EventEmitter kann MyClass Ereignisse wie diese ausgeben:
this.emit("custom event", "argument 1", "argument 2");
};
Wenn im obigen Code die someMethod-Methode von einer Instanz von MyClass aufgerufen wird, wird ein Ereignis namens „cuteom event“ ausgegeben. Dieses Ereignis gibt auch zwei Zeichenfolgen als Daten aus: „Argument 1“ und „Argument 2“. , werden sie als Parameter an den Ereignis-Listener übergeben.
Clients von MyClass-Instanzen können auf „benutzerdefinierte Ereignisse“ wie diese warten:
myInstance.on('custom event', function(str1, str2) {
console.log('habe ein benutzerdefiniertes Ereignis mit str1 %s und str2 %s!', str1, str2);
});
Als weiteres Beispiel können Sie eine Ticker-Klasse erstellen, die jede Sekunde ein „Tick“-Ereignis ausgibt:
EventEmitter = require('events').EventEmitter;
var Ticker = function() {
var self = this;
setInterval(function() {
self.emit('tick');
}, 1000);
};
util.inherits(Ticker, EventEmitter);
Der Client, der die Ticker-Klasse verwendet, kann zeigen, wie die Ticker-Klasse verwendet wird, und auf das „Tick“-Ereignis warten,
ticker.on("tick", function() {
console.log("tick");
});
Zusammenfassung
Das Ereignis-Emitter-Muster ist ein wiedereintrittsfähiges Muster, das verwendet werden kann, um ein Ereignis-Emitter-Objekt von einem Satz ereignisspezifischen Codes zu entkoppeln.
Sie können event_emitter.on() verwenden, um einen Listener für einen bestimmten Ereignistyp zu registrieren, und event_emitter.removeListener(), um die Registrierung aufzuheben.
Sie können auch Ihren eigenen Event-Emitter erstellen, indem Sie EventEmitter erben und einfach die Funktion .emit() verwenden.