‫Jonas: Lassen Sie uns
‫jetzt etwas Code schreiben, um ein besseres Verständnis
‫davon zu bekommen, was in der Ereignisschleife passiert.
‫Um mit dem Codierungsteil
‫dieses Abschnitts zu beginnen, holen Sie
‫sich also wie zuvor die Startdateien
‫für diesen Abschnitt, kopieren Sie sie irgendwo
‫und öffnen Sie dann das Projekt in Ihrem Code-Editor.
‫Also habe ich es hier auf dem
‫Desktop und in meinem VSCode, und alles, was wir hier zum
‫Starten haben, ist diese Testdatei. txt, also erstelle ich hier eine neue
‫Datei und nenne sie die Ereignisschleife. js.
‫Okay, jetzt ist es tatsächlich extrem schwierig, die Ereignisschleife
‫richtig zu simulieren, weil wir nicht
‫wirklich viele Rückrufe gleichzeitig in
‫alle Rückrufwarteschlangen stellen
‫können, über die wir zuvor gesprochen haben, oder.
‫Diese Situation tritt auf, wenn
‫beispielsweise viele Anfragen in Ihre App
‫eingehen, aber hier lokal nur sehr schwer zu replizieren.
‫Aber wir werden trotzdem einige sehr interessante Experimente
‫mit einigen der Dinge machen, die wir
‫im letzten Video gelernt haben.
‫Und was wir tun werden, ist,
‫eine Reihe von Codezeilen zu schreiben und
‫dann herauszufinden, in welcher Reihenfolge sie in der Ereignisschleife
‫ausgeführt werden sollten, und zu analysieren, ob die
‫Ergebnisse, die wir erhalten, tatsächlich Sinn machen.
‫Beginnen wir also damit, setTimeout zu schreiben,
‫also im Grunde, um einen Timer
‫zu setzen, und ich verwende
‫hier eine Pfeilfunktion,
‫und das einzige, was ich tun werde,
‫ist, einen String zu loggen, also Konsole. log, Timer eins ist abgelaufen,
‫und ich möchte, dass dieser nach null Sekunden abläuft.
‫Okay, speichern Sie es, und dann sehen
‫Sie, dass diese Zeichenfolge hier tatsächlich korrigiert
‫wurde, also diese Anführungszeichen geändert und das Semikolon hinzugefügt wurde,
‫und das liegt an
‫der Prettier-Erweiterung, die wir zuvor installiert haben.
‫Jetzt werde ich es hier nicht
‫konfigurieren, denn für dieses
‫kleine Beispiel ist das nicht wirklich notwendig, okay.
‫Das ist also das Einstellen eines Timers, dann
‫verwenden wir auch setImmediate, also merken Sie sich das aus dem letzten Video.
‫Okay, jetzt werde ich hier nicht viel erklären,
‫denn jetzt lass uns diesen
‫Code schreiben und dann analysieren, was passiert, okay.
‫Also setImmediate, und genau wie der andere Timer erhält
‫er eine Callback-Funktion.
‫Und alles, was wir tun werden, ist,
‫sofort ein fertiges zu schreiben, und dieses hat keine Zeit,
‫weil es nicht notwendig
‫ist, weil es, wie der Name sagt, sofort ist.
‫Eine andere Sache, die wir
‫tun möchten, ist, diese Datei hier zu lesen
‫und dann eine Callback-Funktion zu übergeben, okay, also als
‫erstes müssen wir das richtige Modul benötigen, also FS und jetzt
‫fs. readFile, und wir wollen text-file. txt,
‫und dann passiert etwas.
‫Und genau wie zuvor, sagen wir
‫hier einfach, in diesem Fall I/O beendet, in Ordnung.
‫Also I/O, weil dieser Callback
‫hier eigentlich mit einer I/O-Aufgabe zusammenhängt, die tatsächlich
‫ihre eigene Phase in der Ereignisschleife bekommt, richtig.
‫Und schließlich fügen wir einfach eine Konsole hinzu. Melden Sie sich
‫hier an und sagen Sie gut, hallo vom Top-Level-Code.
‫Okay, also Top-Level-Code, denn er ist tatsächlich der einzige,
‫der nicht in einem Callback enthalten ist.
‫Lassen Sie uns in diesem sehr kleinen Beispiel, das wir
‫jetzt haben, versuchen herauszufinden, was zuerst passieren sollte.
‫Denken Sie daran, dass der erste Schritt
‫beim Laden eines Moduls darin besteht, dass der Code
‫der obersten Ebene ausgeführt wird, also sollten wir
‫sofort diese Konsole sehen. log hier, richtig, erst
‫später sollten wir die anderen Protokolle sehen, die
‫von diesen anderen Rückrufen stammen, da
‫diese Rückrufe tatsächlich in der Ereignisschleife ausgeführt werden.
‫Versuchen wir also zu sehen, ob das tatsächlich der
‫Fall ist, und analysieren wir es dann sogar noch ein bisschen.
‫Also öffne die Konsole hier und wieder mache
‫ich das mit dem
‫Terminal, oder tatsächlich in der Ansicht, also dieses Terminal hier, in Ordnung.
‫Beachten Sie also die Ereignisschleife. js, oder bevor wir
‫dies tun, muss ich diesen Tippfehler hier korrigieren, also
‫ist es eine Testdatei, keine Textdatei.
‫Okay, aber jetzt sind wir bereit, diesen
‫Befehl auszuführen, und die Ergebnisse sind da, also ist das
‫erste Protokoll, das wir hier haben, hallo vom Code der obersten
‫Ebene, und das wurde erwartet, richtig,
‫denn dies ist ein Code, der ausgeführt wird sofort.
‫Danach haben wir diese drei Ausgaben, aber sie sind
‫eigentlich in keiner bestimmten Reihenfolge, und
‫das liegt daran, dass dieser
‫Code hier, also dieser Code, tatsächlich nicht in einem
‫E/A-Zyklus ist, also nicht innerhalb der Ereignisschleife
‫läuft. weil es nicht innerhalb einer Rückruffunktion ausgeführt wird.
‫Okay, auf Ihrem Computer sehen Sie dieses Protokoll möglicherweise tatsächlich
‫vor diesem, und dann
‫ist dieses hier nur das letzte,
‫da das Lesen dieser großen Datei hier einige Zeit
‫in Anspruch nimmt und es daher wahrscheinlich
‫immer das letzte sein wird eins.
‫Auch hier hat die Reihenfolge dieser drei hier noch einmal nichts
‫mit der Ereignisschleife zu tun, da
‫sie tatsächlich noch nicht
‫in der Ereignisschleife ausgeführt werden.
‫Dazu müssen wir sie oder
‫zumindest diese beiden hier in eine Callback-Funktion verschieben.
‫Und dafür werden wir diesen hier tatsächlich verwenden.
‫Also lass es mich hier setzen und sagen Timer zwei und Timer zwei
‫hier oder sofort zwei, und ich werde auch einen weiteren Timer hier hinzufügen, also
‫Timer drei, und dieser wird drei
‫Sekunden lang laufen, das sind also 3000 Millisekunden .
‫Lassen Sie uns nun das Ergebnis von diesem versuchen.
‫Aber bevor wir das tun,
‫überlegen wir uns, warum das Programm nach dem Ausführen
‫des gesamten Codes zuvor beendet wurde und zur Eingabeaufforderung hier zurückkehrte.
‫Denken Sie also daran, wie Node. js entscheidet, ob es
‫die Ereignisschleife weiter ausführen soll, naja, es tut
‫dies, indem es fragt, ob noch Zeit im Hintergrund
‫läuft, und wenn ja, wird es nicht beendet, und wenn
‫es noch einen ausstehenden Timer gibt, dann
‫ist es das nicht werde das Programm verlassen.
‫Aber wenn nicht, was im
‫ersten Beispiel hier der Fall war, dann beendet es
‫das Programm sofort, okay, aber jetzt haben wir
‫hier tatsächlich einen Timer, also läuft dieser
‫drei Sekunden lang, und so versuchen wir herauszufinden,
‫was passiert hier.
‫Okay, Sie sehen, dass es
‫nicht beendet wird, und erst jetzt, nachdem Timer drei
‫abgelaufen ist, hat es die Anwendung beendet.
‫Okay, sehen wir uns das noch einmal
‫an, also alle diese Log-Timer laufen, der Timer ist
‫beendet und die Anwendung wurde beendet.
‫Nun zu diesen Ergebnissen hier, lassen
‫Sie uns dies hier etwas
‫einfacher machen und hier nur ein kleines Trennzeichen hinzufügen.
‫Okay, mach das sauber, okay.
‫Diese vier hier sind also die Ausgaben, die
‫nicht wirklich in der Ereignisschleife ausgeführt wurden,
‫aber diese drei hier kamen
‫tatsächlich aus der Ereignisschleife. Lassen Sie uns nun diese Ergebnisse analysieren.
‫Wenn Sie sich
‫jetzt an das Diagramm aus der vorherigen
‫Vorlesung erinnern, werden Sie wahrscheinlich gedacht haben, dass der Timer, hier
‫also Timer zwei, eigentlich vor dem
‫setImmediate enden sollte, da er im Diagramm
‫tatsächlich ganz oben auf der Ereignisschleife.
‫Also müssen wir hier timeout mit
‫null setzen, was ungefähr gleich setImmediate sein sollte, richtig.
‫Warum erscheint
‫setImmediate eigentlich vor setTimeout?
‫Nun, es gibt etwas, das ich
‫im letzten Video nicht wirklich erklärt habe, weil
‫ich es nicht noch verwirrender machen wollte, und das ist, dass
‫die Ereignisschleife tatsächlich darauf wartet, dass in der Umfragephase etwas passiert.
‫Also in dieser Phase, in der I/O-Callbacks bearbeitet werden.
‫Wenn also diese Warteschlange von Callbacks leer ist,
‫was in unserem fiktiven Beispiel hier
‫der Fall ist, wir also
‫keine I/O-Callbacks haben, haben wir nur diese Timer, nun,
‫dann wartet die Ereignisschleife in dieser Phase, bis eine abgelaufene Timer.
‫Wenn wir jedoch einen Callback mit setImmediate geplant
‫haben, wird dieser Callback tatsächlich direkt
‫nach der Polling-Phase ausgeführt,
‫und sogar vor abgelaufenen Timern, falls vorhanden.
‫Und in diesem Fall läuft der Timer sofort ab,
‫also nach null Sekunden, aber auch hier
‫wartet die Ereignisschleife tatsächlich, also pausiert sie in der Polling-Phase.
‫Und damit der setImmediate-Callback tatsächlich
‫zuerst ausgeführt wird, ist das der ganze Grund, warum
‫wir dies sofort hier haben, nachdem wir die Timer haben.
‫Okay, und ich weiß, das klingt sehr verwirrend,
‫und ich stimme voll und
‫ganz zu, aber Node. js funktioniert.
‫Und jetzt machen wir das noch ein bisschen verwirrender und
‫fügen den Prozess hinzu. nextTick, über den
‫wir auch in der letzten Vorlesung gesprochen haben.
‫Wir sagen also Prozess. nextTick, und dann übergeben wir ihm
‫wie zuvor einfach eine Callback-Funktion.
‫Und wieder möchte ich mich nur bei
‫der Konsole anmelden und sage Prozess. nextTick.
‫Was denkst du wird also in diesem Fall passieren?
‫Machen Sie eine Vermutung, und sobald Sie bereit sind, können Sie dies
‫ausführen, also lassen Sie uns das tun.
‫Zuerst hier speichern, ausführen und los geht's.
‫Die Ergebnisse sind also wieder drin, und nun
‫war der erste Callback, der ausgeführt wurde, tatsächlich diese Funktion hier.
‫Deshalb haben wir einen Prozess. WeiterHäkchen direkt
‫im ersten an.
‫Warum also war diese Callback-Funktion von process. nextKreuzen Sie die erste
‫von allen an, die ausgeführt werden soll?
‫Denken Sie daran, dass nextTick
‫Teil der Microtasks-Warteschlange ist, die nach jeder
‫Phase ausgeführt wird, also nicht nur nach einem ganzen Tick.
‫Und was hier passiert ist, ist,
‫dass diese Callback-Funktion tatsächlich vor der Phase ausgeführt wurde, in der diese
‫Callback-Funktion hier ausgeführt wurde, und der Phase davor, okay.
‫Nun, nextTick ist eigentlich ein irreführender Name, denn ein
‫Tick ist eigentlich eine ganze Schleife,
‫aber nextTick passiert tatsächlich vor der nächsten Schleifenphase und
‫nicht der gesamte Tick, also habe ich das vorher gesagt.
‫Auf der anderen Seite würde setImmediate Sie denken
‫lassen, dass sein Callback sofort ausgeführt würde, aber
‫das tut es nicht, richtig, also
‫wird setImmediate tatsächlich einmal pro
‫Tick ausgeführt, während nextTick sofort ausgeführt wird.
‫Und so sollten ihre beiden Namen eigentlich vertauscht werden.
‫Sie sollten umgekehrt sein, und dies kann
‫eine Menge Verwirrung stiften, daher
‫ist es am besten, sich immer an eine von ihnen zu halten,
‫und diese ist normalerweise sofort und nicht verarbeiten. nextTick.
‫Wie auch immer, wie ich in einem letzten
‫Video erwähnt habe, sind diese Mechanismen eigentlich eher für ziemlich fortgeschrittene
‫Anwendungsfälle gedacht, weshalb wir sie
‫in unserem Projekt wahrscheinlich nicht verwenden werden.
‫Wenn Sie jedoch mehr darüber
‫erfahren möchten, können Sie jederzeit die offizielle Node-Dokumentation lesen.
‫Okay, damit haben wir einige Aspekte der
‫Event-Schleife simuliert, aber zum Schluss möchte ich
‫noch kurz etwas zum Thread-Pool
‫vorstellen, und dafür werden wir eine komplexere
‫Operation durchführen, die tatsächlich ausgelagert wird den
‫Thread-Pool, und werfen Sie einen Blick
‫darauf, wie lange die Ausführung dieser Vorgänge dauert und
‫wie wir
‫die zuvor besprochene Thread-Pool-Größe ändern können.
‫Wir werden hier also etwas Kryptographie
‫verwenden, um ein Passwort im Grunde zu verschlüsseln.
‫Das ist also ein neues Paket, das
‫wir vorher noch nicht verwendet haben, und es heißt Crypto,
‫aber ich habe bereits erwähnt, dass alle Funktionen dieses
‫Pakets automatisch von der Ereignisschleife in den Thread-Pool ausgelagert werden,
‫und das ist es, was wir
‫Werde es jetzt testen.
‫Und lass uns das jetzt verwenden, also Krypto. pb, also werden wir eine
‫Verschlüsselungsfunktion namens pbkdf2 verwenden, so ein seltsamer Name, und
‫jetzt übergeben wir den geheimen String, der einfach Passwort
‫sagen soll, dann brauchen wir einen String, um die Passwörter
‫zu lösen, und die Implementierungen hier spielt
‫keine Rolle, also werde ich jetzt nicht ins Detail
‫gehen, wie das funktioniert, das
‫machen wir später, okay, hier brauchen wir nur
‫die Schlüssellänge, also ist diese Zahl
‫hier nur für
‫die Anzahl der Iterationen, dann ist dies die Schlüssellänge und
‫schließlich der Algorithmus, der verwendet
‫wird, um das Passwort zu verschlüsseln.
‫Diese Zahlen erhöhen also einfach die
‫Zeit, die für die Verschlüsselung benötigt wird.
‫Okay, und schließlich braucht es auch eine Callback-Funktion, und alles, was ich hier tun werde, ist
‫eine andere Konsole zu tun. log,
‫und sagen Sie einfach Passwort verschlüsselt.
‫Nun, zum Zweck dieser Demonstration möchte
‫ich Ihnen eigentlich zeigen, wie viel Zeit
‫jede dieser Operationen in Anspruch nimmt. Wir
‫werden also zu Beginn eine Variable im Grunde definieren und
‫auf das Datum setzen. Nun, das ist das aktuelle Datum
‫in Millisekunden, und hier müssen wir nur noch nicht nur die
‫Zeichenfolge, sondern auch das Datum protokollieren. Nun, dies ist das Datum an
‫diesem Punkt in Millisekunden, abzüglich des Starts,
‫und so erhalten wir die Anzahl der
‫Millisekunden, die vergangen sind, um
‫diese Berechnung durchzuführen.
‫Okay, also probieren wir
‫es aus, speichern es und führen es erneut aus, und hier
‫sehen Sie, dass es 1855 Millisekunden, also
‫fast zwei Sekunden dauerte, um dieses Passwort zu verschlüsseln.
‫Okay, aber jetzt lassen Sie uns diesen Code tatsächlich
‫duplizieren, oder eigentlich möchte ich vier
‫Instanzen davon, um Ihnen etwas zu zeigen.
‫Wenn wir das jetzt also ausführen, sollte es wahrscheinlich ungefähr gleich lange
‫dauern, und ja, das tut es, also
‫war es 1. 8 Sekunden,
‫jetzt sind es 2. 2 Sekunden, was ziemlich ähnlich ist.
‫Okay, und denken Sie daran, dass ich Ihnen
‫in einer der früheren Vorlesungen
‫gesagt habe, dass die Größe des Thread-Pools
‫standardmäßig vier ist, also vier Threads
‫gleichzeitig die Arbeit erledigen, und
‫deshalb dauern diese vier Passwortverschlüsselungen
‫ungefähr zur gleichen
‫Zeit und passieren im Grunde alle gleichzeitig.
‫Aber wir können diese Thread-Pool-Größe tatsächlich ändern.
‫Lassen Sie mich Ihnen also zeigen, wie wir das tun, und wir tun
‫dies, indem wir Prozess sagen. env, und
‫das ist eine Umgebungsvariable, und
‫darüber werden wir später noch einmal
‫sprechen, und UV, was
‫für LibUV steht, Threadpool-Größe. Wenn wir dies also auf eins setzen,
‫haben wir nur einen Thread in unserem Thread-Pool.
‫Versuchen wir es noch einmal und sehen wir, was jetzt passiert.
‫Sie sehen also, dass sie alle viel länger brauchen, um sie zu berechnen.
‫Grundsätzlich werden sie nacheinander berechnet.
‫Also dieser die erste, dann dieser hier dauert die
‫doppelte Zeit, und das liegt wiederum
‫daran, dass dieser erst beginnen kann, sobald der
‫erste fertig ist, und der gleiche mit
‫dem dritten und vierten.
‫Nehmen wir nun an,
‫wir ändern es auf zwei, dann sollten
‫wir die ersten beiden mit derselben Zeit sehen und
‫genau die zweiten beiden auch mit einer ähnlichen Zeit.
‫Das macht also Sinn, richtig.
‫Nur der Vollständigkeit halber
‫hier jetzt gleich mit drei, also haben diese drei
‫eine ähnliche Zeit, und dann die letzte.
‫Okay, so funktioniert der Thread-Pool im Grunde, also habe ich mich
‫entschieden, mich nicht nur auf die Ereignisschleife zu
‫konzentrieren, sondern Ihnen auch einen schnellen Überblick über den Thread-Pool zu
‫geben und wie wir die Thread-Pool-Größe
‫ändern können , okay, bedenken Sie, dass
‫dieser Code hier eigentlich
‫immer noch asynchron ist, weil wir eine Callback-Funktion übergeben haben.
‫Okay, und wir könnten tatsächlich die synchrone Version verwenden, aber
‫das würde dann die Ereignisschleife blockieren.
‫Willst du das sehen?
‫Also lass mich dir das auch ganz schnell zeigen, so
‫dass einer nicht wirklich geplant war, aber
‫machen wir es trotzdem, denn es ist sehr
‫interessant zu sehen, wie ich finde.
‫Okay, das wird so funktionieren.
‫Wir verwenden wieder die Sync-Version der gleichen Funktion,
‫die oft zur Verfügung steht.
‫Denken Sie daran, dass wir dasselbe für die read
‫file haben, also haben wir read file,
‫die einen Rückruf akzeptiert, und wir haben read file sync,
‫die synchrone Version, die keinen Rückruf akzeptiert, also einfach so.
‫Dieser verschlüsselt dann das Passwort,
‫blockiert die Codeausführung und geht dann zur nächsten Zeile über,
‫die dies dann hier an der Konsole protokolliert.
‫Lassen Sie uns das jetzt hier duplizieren.
‫Das war zu viel, lösche dieses
‫hier, und so laufen diese vier Passwortverschlüsselungen jetzt nicht
‫mehr in der Ereignisschleife.
‫Daher werden sie nicht mehr in den Thread-Pool ausgelagert.
‫Okay, und jetzt, selbst wenn wir die Thread-Pool-Größe von
‫vier haben, wollen wir sehen, was passiert.
‫Sie sehen also, sie laufen völlig synchron ab.
‫Also einer nach dem anderen, und was noch
‫schlimmer ist, ist, dass
‫alle diese Timer hier, sogar nextTick
‫und der unmittelbare, erst auftauchten, nachdem die Passwortverschlüsselungen stattgefunden hatten.
‫Das blockierte also wirklich die gesamte Ausführung, auch
‫wenn dieser Timer hier, zum Beispiel dieser
‫hier, nach null Sekunden beendet
‫war und dieser nach
‫drei Sekunden, aber alle müssen sieben
‫Sekunden warten, bis all diese
‫Passwortverschlüsselungen hier waren endlich fertig, wieder weil sie nicht innerhalb
‫der Ereignisschleife liefen, aber diese waren.
‫Und so arbeiteten sie im
‫Grunde im
‫Hintergrund und konnten erst
‫von der Ereignisschleife erfasst
‫werden, wenn sie bereit
‫waren, direkt nach diesen vier Passwortverschlüsselungen.
‫Okay, das war genug für einen Vortrag, ich hoffe, Sie
‫fanden ihn interessant, und
‫wir sehen uns gleich im nächsten.