‫Kursleiter: Willkommen zurück.
‫Lassen Sie uns nun alles über die Ereignisschleife erfahren,
‫die das Herzstück der Node. js-Architektur.
‫Und dies ist wahrscheinlich der wichtigste Vortrag
‫in diesem Abschnitt, also stellen Sie sicher, dass Sie
‫wirklich alles verstehen, was ich Ihnen in diesem Video zeige.
‫Also lasst uns anfangen.
‫Also, hier ist ein Diagramm ähnlich wie in der
‫letzten Vorlesung, damit wir genau wissen, wovon wir hier sprechen.
‫Wir befinden uns also immer noch in einem Node-Prozess in dem einzelnen
‫Thread, in dem die Ereignisschleife ausgeführt wird, in Ordnung?
‫Als erstes müssen Sie
‫wissen, dass in der Ereignisschleife
‫der gesamte Anwendungscode
‫ausgeführt wird, der sich in Callback-Funktionen befindet.
‫Grundsätzlich wird also jeder Code, der kein Top-Level-Code ist, in
‫der Ereignisschleife ausgeführt.
‫Einige Teile werden möglicherweise in den Thread-Pool ausgelagert, wie wir in
‫der letzten Vorlesung gesehen haben,
‫aber es ist die Ereignisschleife, die sich um all das kümmert.
‫Wie ich bereits sagte, ist es wirklich
‫das Herz der Node-Architektur.
‫Okay, wie ich im ersten Teil des Kurses schon oft
‫erwähnt habe, Node. js
‫basiert auf Callback-Funktionen.
‫Also Funktionen, die aufgerufen werden,
‫sobald eine Arbeit irgendwann in der Zukunft abgeschlossen ist.
‫Erinnere dich daran?
‫Und das funktioniert so, weil
‫Node eine ereignisgesteuerte Architektur verwendet,
‫worüber wir in einem der nächsten
‫Videos sprechen werden.
‫Aber was Sie vorerst wissen
‫müssen, ist, dass Dinge wie
‫unsere Anwendung, die eine HTTP-Anfrage auf unserem Server erhält oder ein Timer abläuft oder
‫eine Datei zum Lesen beendet wird, alle diese Ereignisse ausgeben,
‫sobald sie mit ihrer Arbeit fertig sind,
‫und unser Ereignis loop nimmt dann diese Ereignisse
‫auf und ruft
‫die Callback-Funktionen auf, die jedem Ereignis zugeordnet sind.
‫Okay, sinnvoll?
‫Auch hier empfängt die Ereignisschleife jedes Mal Ereignisse, wenn
‫etwas Wichtiges passiert, und ruft dann
‫die erforderlichen Rückrufe auf, wie wir sie
‫in unserem Code definieren.
‫Zusammenfassend heißt es normalerweise, dass
‫die Ereignisschleife die Orchestrierung übernimmt, was
‫einfach bedeutet, dass sie Ereignisse empfängt,
‫ihre Rückruffunktionen aufruft
‫und die teureren Aufgaben in den Thread-Pool auslagert.
‫Wie funktioniert das alles eigentlich hinter den Kulissen?
‫In welcher Reihenfolge werden diese Callbacks ausgeführt?
‫Nun, das werden wir als nächstes herausfinden.
‫Denken Sie also daran, dass die Ereignisschleife sofort
‫ausgeführt wird, wenn wir unsere Node-Anwendung starten.
‫Jetzt hat die Ereignisschleife mehrere Phasen,
‫und jede Phase hat eine Rückrufwarteschlange, die
‫die Rückrufe sind, die von den Ereignissen stammen,
‫die die Ereignisschleife empfängt,
‫genau wie wir auf der letzten Folie besprochen haben.
‫Nun werden Sie an
‫manchen Stellen lesen, dass es nur eine Rückrufwarteschlange oder eine
‫Ereigniswarteschlange gibt, aber tatsächlich hat die Ereignisschleife, wie gesagt, viele
‫Phasen, wobei jede Phase ihre eigene Rückrufwarteschlange hat.
‫Werfen wir nun einen Blick auf die vier wichtigsten Phasen.
‫Es gibt ein oder zwei andere Phasen,
‫die intern von Node verwendet werden,
‫aber diese sind nicht
‫so wichtig und ich werde nicht darüber sprechen.
‫Die erste Phase
‫kümmert sich also um Rückrufe
‫abgelaufener Timer, beispielsweise aus der Funktion setTimeout().
‫Wenn es also Callback-Funktionen von gerade
‫abgelaufenen Timern gibt, werden diese als erstes
‫von der Ereignisschleife verarbeitet.
‫Wenn ein Timer später während der Verarbeitung
‫einer der anderen Phasen
‫abläuft, wird der Callback dieses Timers nur aufgerufen,
‫sobald die Ereignisschleife zu dieser ersten Phase zurückkehrt.
‫Sinn ergeben?
‫Und das funktioniert in allen vier Phasen so.
‫Rückrufe in jeder Warteschlange werden also
‫nacheinander verarbeitet, bis sich keine mehr in der Warteschlange befinden,
‫und erst dann tritt die Ereignisschleife in die nächste Phase ein.
‫Als nächstes haben wir das E/A-Polling und die Ausführung von E/A-Callbacks.
‫Denken Sie auch hier daran, dass I/O für Input/Output steht.
‫Polling bedeutet also im Grunde, nach neuen I/O-Ereignissen zu
‫suchen, die zur Verarbeitung bereit
‫sind, und diese in die Callback-Warteschlange zu stellen.
‫Und denken Sie daran, dass I/O im
‫Kontext einer Node-Anwendung hauptsächlich Dinge wie Netzwerk- und Dateizugriff bedeutet.
‫In dieser Phase werden wahrscheinlich 99% unseres Codes
‫ausgeführt, einfach weil
‫in einer typischen Node-App der
‫Großteil Was wir tun müssen, bezieht sich auf die
‫Vernetzung und auch auf den Dateizugriff.
‫Die nächste Phase ist für setImmediate-Callbacks und setImmediate
‫ist eine spezielle Art von
‫Timer, die wir verwenden können, wenn wir Callbacks unmittelbar
‫nach der E/A-Polling- und Ausführungsphase verarbeiten möchten, was
‫in einigen fortgeschritteneren Anwendungsfällen wichtig sein kann.
‫Gut.
‫Und schließlich ist die vierte Phase für enge Rückrufe, die für
‫uns wiederum nicht so wichtig sind,
‫aber der Vollständigkeit halber schreibe ich dies trotzdem hier.
‫Grundsätzlich werden in dieser Phase alle Close-Events
‫verarbeitet, beispielsweise wenn ein Webserver oder ein WebSocket heruntergefahren wird.
‫Das sind also die vier Phasen in der
‫Ereignisschleife, aber neben diesen vier Callback-Warteschlangen, die wir gerade
‫gesehen haben, gibt es tatsächlich noch
‫zwei weitere Warteschlangen, die nextTick()-Warteschlange und die andere
‫Microtasks-Warteschlange, die hauptsächlich für aufgelöste Promises gedacht ist.
‫Wenn Sie mit Versprechen nicht vertraut
‫sind, werden wir in einem späteren Abschnitt ein wenig darüber sprechen.
‫Wenn in einer dieser beiden Warteschlangen
‫Callbacks verarbeitet werden müssen, werden
‫diese jedoch direkt nach Abschluss der aktuellen Phase
‫der Ereignisschleife ausgeführt, anstatt auf das Ende
‫der gesamten Schleife zu warten.
‫Okay?
‫Mit anderen Worten, nach jeder dieser vier
‫Phasen werden alle Rückrufe in diesen beiden
‫speziellen Warteschlangen sofort ausgeführt.
‫Stellen Sie sich nun beispielsweise vor, dass ein
‫Promise aufgelöst wird und einige Daten von einem API-Aufruf zurückgibt,
‫während der Rückruf eines abgelaufenen Timers läuft.
‫In diesem Fall wird der Promise-Callback also direkt
‫nach dem Ablauf des Timers ausgeführt.
‫Okay?
‫Und die gleiche Logik gilt auch für die nextTick()-Warteschlange, über
‫die wir noch nicht gesprochen haben.
‫Im Grunde ist process the nextTick() also eine Funktion, die
‫wir verwenden können, wenn wir wirklich,
‫wirklich einen bestimmten Callback direkt
‫nach der aktuellen Ereignisschleifenphase ausführen müssen.
‫Es ist setImmediate ähnlich, mit dem
‫Unterschied, dass setImmediate erst nach der
‫I/O-Callback-Phase ausgeführt wird.
‫Was jedoch ähnlich
‫ist, ist, dass beide für wirklich fortgeschrittene Anwendungsfälle gedacht sind
‫und wir sie in diesem Kurs wahrscheinlich
‫nicht einmal benötigen werden.
‫Aber wie auch immer, ich wollte auch diese komplexeren
‫Dinge hier einschließen, damit Sie die
‫Werkzeuge haben, die Sie brauchen, wenn Sie
‫wirklich tief in Node. js, wenn Sie möchten.
‫In Ordnung, und damit haben wir tatsächlich einen Tick
‫der Ereignisschleife beendet, und ein Tick ist im Grunde nur ein Zyklus
‫in dieser Schleife.
‫Jetzt ist es also an der Zeit zu entscheiden, ob die Schleife
‫bis zum nächsten Tick fortgesetzt werden soll oder ob das Programm beendet werden soll.
‫Und wie macht Node das?
‫Nun, es ist ganz einfach.
‫Node prüft einfach, ob noch
‫Timer oder I/O-Aufgaben im Hintergrund laufen, und
‫wenn keine vorhanden sind, wird die Anwendung beendet.
‫Aber wenn es irgendwelche ausstehenden Timer oder I/O-Aufgaben gibt,
‫dann wird es die Ereignisschleife weiter
‫ausführen und direkt zum nächsten Tick gehen.
‫Wenn wir also beispielsweise
‫auf eingehende HTTP-Anforderungen lauschen, wie wir es
‫in unserem Node-Farmprojekt in einem vorherigen Abschnitt getan haben,
‫haben wir im Grunde eine E/A-Aufgabe ausgeführt, und
‫deshalb haben die Ereignisschleife und
‫damit Node. js, laufen
‫Sie weiter und hören Sie auf neue HTTP-Anforderungen, die
‫eintreffen, anstatt nur die Anwendung zu beenden.
‫Wenn wir im Hintergrund eine Datei
‫schreiben oder lesen, ist das auch eine E/A-Aufgabe, und
‫daher ist es sinnvoll, dass die App nicht
‫beendet wird, während sie mit dieser Datei arbeitet, oder?
‫Okay, und das ist im Grunde das, was Sie über
‫den Node wissen sollten. js-Ereignisschleife.
‫Wenn Sie noch mehr Details benötigen, können
‫Sie jederzeit versuchen,
‫die offizielle Node-Dokumentation zu lesen, die für Sie an
‫dieser Stelle recht leicht verständlich sein
‫sollte, da Sie ohnehin den größten Teil der Ereignisschleife verstehen.
‫Und ich möchte nur betonen,
‫dass es wirklich sehr wichtig für Sie ist, die
‫Ereignisschleife richtig zu verstehen, damit Sie Ihren
‫eigenen Leistungscode schreiben und auch Ihren eigenen Code debuggen
‫können, wenn etwas auf unerwartete Weise schief geht.
‫Und jetzt, zum Abschluss, lassen Sie uns einige der Dinge, über
‫die wir hier gesprochen haben, noch einmal Revue passieren lassen.
‫Kurz gesagt, das Wichtigste, was Sie in dieser
‫Vorlesung und vielleicht auch in diesem
‫gesamten Kurs verstehen sollten, ist, dass
‫die Ereignisschleife die asynchrone Programmierung
‫in Node ermöglicht. js, was es
‫zum wichtigsten Feature im Design von Node macht
‫und Node. js völlig
‫anders als andere Plattformen.
‫Es kümmert sich um alle eingehenden
‫Ereignisse und führt die Orchestrierung durch, indem es schwerere Aufgaben
‫in den Thread-Pool auslagert und die einfachste Arbeit selbst erledigt.
‫Denken Sie auch daran, dass wir die Ereignisschleife
‫benötigen, da in Node. js funktioniert alles in
‫einem einzigen Thread, und so
‫können Tausende oder Millionen von Benutzern gleichzeitig auf denselben Thread zugreifen.
‫Dies macht Node so leicht und skalierbar,
‫birgt aber gleichzeitig die Gefahr, dass unser
‫einzelner Thread blockiert
‫wird, was die gesamte App verlangsamen
‫oder sogar für alle Ihre Benutzer, die auf die App zugreifen, stoppen würde, richtig?
‫In anderen Sprachen wie PHP, das
‫auf einem Apache-Server läuft, wird im Grunde genommen für
‫jeden neuen Benutzer ein neuer Thread erstellt, was viel ressourcenintensiver ist.
‫Auf der anderen Seite besteht
‫aber keine Blockierungsgefahr, oder?
‫Dieses ganze Modell macht es Anfängern etwas
‫einfacher, PHP zu verwenden, aber es hat natürlich auch
‫seine eigenen Nachteile, auf die
‫ich an dieser Stelle nicht eingehen werde.
‫Wie auch immer, lassen Sie
‫mich Sie noch einmal daran erinnern, dass es in Ihrer Verantwortung liegt, die Ereignisschleife
‫nicht zu blockieren, und deshalb hier ein paar Richtlinien dafür.
‫Verwenden Sie zunächst nicht
‫die Sync-Versionen der Funktionen in den
‫Modulen fs, crypto oder zlib in Ihren Callback-Funktionen, okay?
‫In unserem ersten Projekt haben wir also tatsächlich
‫die synchrone Version verwendet, aber sie war im Code der
‫obersten Ebene, also außerhalb jedes Rückrufs.
‫Und da dieser Code ausgeführt wird, bevor die Ereignisschleife überhaupt
‫beginnt, ist es kein Problem, dort die synchrone Version zu verwenden.
‫Außerdem, und das ist wahrscheinlich ziemlich offensichtlich,
‫führen Sie keine sehr komplexen Berechnungen in der Ereignisschleife durch.
‫Also, Sachen wie das
‫Knirschen von Millionen von Zahlen in Schleifen innerhalb von Schleifen oder so ähnlich.
‫Seien Sie als Nächstes vorsichtig mit JSON in sehr großen
‫Objekten, da das Parsen oder Stringifizieren von
‫JSON zu einem bestimmten Zeitpunkt lange dauern kann.
‫Und schließlich sollten Sie keine allzu komplexen regulären Ausdrücke
‫verwenden, beispielsweise mit mehreren verschachtelten Quantoren
‫oder Rückverweisen, da diese
‫wiederum länger dauern können als erwartet.
‫Dies sind natürlich nur ein paar
‫allgemeine Richtlinien, aber sie werden Sie auf den richtigen
‫Weg bringen.
‫Nun gibt es einige
‫mögliche Lösungen für diese Blockierungsprobleme, wie das manuelle Auslagern
‫in den Thread-Pool oder die Verwendung von untergeordneten Prozessen, und wir
‫könnten am Ende des Kurses oder irgendwann
‫in der Zukunft darüber sprechen, aber
‫im Moment ist es wichtig, dass Sie verstehen und befolgen
‫diesen Rat, die Ereignisschleife wirklich nicht zu blockieren.
‫Gut.
‫Als nächstes werde ich Ihnen ein kleines Beispiel geben, um Ihnen einige
‫der Dinge zu zeigen, über
‫die wir in der Praxis gesprochen haben.