Heim > Web-Frontend > js-Tutorial > Die Ereignisschleife von Node.js: Die Anleitung eines Entwicklers zu Konzepten & Code

Die Ereignisschleife von Node.js: Die Anleitung eines Entwicklers zu Konzepten & Code

Christopher Nolan
Freigeben: 2025-02-12 08:36:12
Original
614 Leute haben es durchsucht

asynchrones Programmieren von Node.js: eingehendes Verständnis von Ereignisschleifen

The Node.js Event Loop: A Developer's Guide to Concepts & Code

asynchrones Programmieren ist in jeder Programmiersprache äußerst schwierig. Konzepte wie Parallelität und Deadlock machen selbst die erfahrensten Ingenieure schwierig. Asynchron ausgeführter Code ist schwer vorherzusagen und schwer zu verfolgen, wenn ein Fehler vorliegt. Dieses Problem ist jedoch unvermeidlich, da das moderne Computing Multi-Core-Prozessoren hat. Jeder CPU-Kern hat eine eigene thermische Grenze, und die Single-Core-Leistungsverbesserung hat einen Engpass erreicht. Dies fordert Entwickler auf, effiziente Code zu schreiben und die Hardware -Ressourcen voll auszunutzen.

JavaScript ist Single-Threaded, aber begrenzt dies die Fähigkeit von Node.js, moderne Architekturen auszunutzen? Eine der größten Herausforderungen besteht darin, sich mit der inhärenten Komplexität des Multithreading zu befassen. Es ist teuer, einen neuen Thread zu erstellen und Kontextwechsel zwischen Threads zu verwalten. Sowohl das Betriebssystem als auch die Programmierer benötigen große Anstrengungen, um eine Lösung zu liefern, die zahlreiche Randfälle abwickelt. In diesem Artikel wird erklärt, wie Node.js dieses Problem durch Ereignisschleifen löst, verschiedene Aspekte von Node.js Ereignisschleifen untersucht und zeigt, wie er funktioniert. Event Loops sind eines der Killer -Funktionen von Node.js, da es dieses schwierige Problem auf völlig neue Weise löst.

Schlüsselpunkte

  • node.js Ereignisschleife ist eine einsthread-, nicht blockierende und asynchrone gleichzeitige Schleife, mit der mehrere Aufgaben effizient bearbeitet werden können, ohne dass jede Aufgabe erledigt wird. Dies ermöglicht es, mehrere Webanforderungen gleichzeitig zu bearbeiten.
  • Ereignisschleife ist semi-infinit, was bedeutet, dass sie beenden kann, wenn der Anrufstack oder die Rückrufwarteschlange leer ist. Diese Schleife ist für die Wahl des Betriebssystems verantwortlich, um Rückrufe von eingehenden Verbindungen zu erhalten.
  • Ereignisschleife wird in mehreren Phasen ausgeführt Rückrufausführung und End -Iteration.
  • node.js verwendet zwei Hauptteile: die V8 JavaScript -Engine und Libuv. Network I/O-, Datei -I/O- und DNS -Abfragen werden über Libuv durchgeführt. Die Anzahl der für diese Aufgaben im Thread -Pool verfügbaren Threads ist begrenzt und kann über die Umgebungsvariable uv_threadpool_size eingestellt werden.
  • Am Ende jeder Phase führt die Schleife den Prozess aus. Der Rückruf von SetImmediate () ist Teil der gesamten Ereignisschleife, so dass er nicht sofort ausgeführt wird, wie der Name impliziert. Es wird im Allgemeinen empfohlen, SetImmediate () zu verwenden.

Was ist eine Ereignisschleife?

Ereignisschleife ist eine einsthread-, nicht blockierende und asynchrone gleichzeitige Schleife. Stellen Sie sich für jemanden ohne Informatik -Abschluss eine Webanfrage vor, die Datenbank -Lookups ausführt. Ein einzelner Thread kann jeweils nur eine Operation durchführen. Anstatt darauf zu warten, dass die Datenbank reagiert, wird weiterhin andere Aufgaben in der Warteschlange bearbeitet. In der Ereignisschleife erweitert die Hauptschleife den Anrufstapel und wartet nicht auf den Rückruf. Da die Schleife nicht blockiert wird, können mehrere Webanforderungen gleichzeitig behandelt werden. Mehrere Anfragen können gleichzeitig in die Warteschlange gestellt werden, wodurch sie gleichzeitig gleichzeitig sind. Die Schleife wartet nicht auf alle Operationen einer Anfrage, die abgeschlossen ist, sondern wird gemäß der Reihenfolge verarbeitet, in der der Rückruf ohne Blockierung auftritt.

Die Schleife selbst ist semi-infinit, was bedeutet, dass sie die Schleife verlassen kann, wenn der Anrufstack oder die Rückrufwarteschlange leer ist. Der Call -Stack kann als Synchroncode wie Console.log angesehen werden, wobei die Erweiterung vor der Umfrage mehr Arbeiten erweitert wird. Node.js verwendet das zugrunde liegende Libuv, um das Betriebssystem für Rückrufe aus eingehenden Verbindungen zu befragen.

Sie fragen sich vielleicht, warum Ereignisschleifen in einem einzigen Thread ausgeführt werden? Themen sind im Speicher relativ schwerer für die für jede Verbindung erforderlichen Daten. Themen sind Betriebssystemressourcen, die gestartet werden müssen, die nicht auf Tausende aktiver Verbindungen ausgedehnt werden müssen.

Normalerweise kann Multithreading auch die Situation komplizieren. Wenn der Rückruf Daten zurückgibt, muss er den Kontext wieder auf den zu verwendenden Thread zurückschüttern. Der Kontextwechsel zwischen Threads ist langsam, da er den aktuellen Zustand synchronisieren muss, z. B. den Anrufstapel oder die lokalen Variablen. Ereignisschleifen können Fehler vermeiden, wenn mehrere Threads Ressourcen teilen, da es ein einzelner Thread ist. Einthread-Schleifen reduzieren fadenkante Fälle und ermöglichen eine schnellere Kontextschaltung. Dies ist das wahre Genie hinter der Schleife. Es verwendet effektiv Verbindungen und Fäden und hält gleichzeitig die Skalierbarkeit bei.

Theorie reicht aus. Sie können es in Reply tun, wie Sie möchten, oder den Quellcode herunterladen.

semi-infinite Schleife

Die größte Frage, die Ereignisschleifen beantworten muss, ist, ob die Schleife aktiv ist. Wenn ja, bestimmen Sie, wie lange Sie in der Rückrufwarteschlange warten können. In jeder Iteration erweitert Loop den Anrufstapel und die Umfragen.

Dies ist ein Beispiel für die Blockierung der Hauptschleife:

setTimeout(
  () => console.log('Hi from the callback queue'),
  5000); // 保持循环活动这么长时间

const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wenn Sie diesen Code ausführen, beachten Sie, dass die Schleife für zwei Sekunden blockiert ist. Die Schleife bleibt jedoch aktiv, bis der Rückruf nach fünf Sekunden ausgeführt wird. Sobald die Hauptschleife entsperrt ist, bestimmt der Wahlmechanismus, wie lange er auf den Rückruf warten wird. Diese Schleife endet, wenn sich der Anrufstapel erweitert und keine Rückrufe übrig sind.

Callback -Warteschlange

Was passiert nun, wenn ich die Hauptschleife blockiere und dann den Rückruf plane? Sobald die Schleife blockiert ist, fügt sie der Warteschlange nicht mehr Rückrufe hinzu:

const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
// 这需要 7 秒才能执行
setTimeout(() => console.log('Ran callback A'), 5000);
Nach dem Login kopieren
Nach dem Login kopieren

Dieser Zyklus bleibt sieben Sekunden lang aktiv. Ereignisschleifen sind in Bezug auf ihre Einfachheit dumm. Es hat keine Möglichkeit zu wissen, was in Zukunft anstehen könnte. In tatsächlichen Systemen werden eingehende Rückrufe in der Warteschlange und Ausführung der Hauptschleife in der Warteschlange und Ausführung. Die Ereignisschleife durchläuft beim Entblockieren mehrere Stufen nacheinander. Um sich in einem Interview über Loops abzuheben, vermeiden Sie ausgefallene Begriffe wie „Event Launcher“ oder „Reaktormodus“. Es ist eine einfache einköpfige Schleife, gleichzeitig und nicht blockierend. Ereignisschleife mit Async/Await

Um zu vermeiden, die Hauptschleife zu blockieren, ist eine Idee, die synchrone E/A mit Async/Warte:

zu wickeln:

const fs = require('fs');
const readFileSync = async (path) => await fs.readFileSync(path);

readFileSync('readme.md').then((data) => console.log(data));
console.log('The event loop continues without blocking...');
Nach dem Login kopieren
Nach dem Login kopieren
Alles, was nach dem Warten erscheint, stammt aus der Rückrufwarteschlange. Der Code sieht aus wie ein synchroner Blockierungscode, blockiert aber nicht. Beachten Sie, dass Async/Auseait ReadFilesync a

dann , das es aus der Hauptschleife entfernt, macht. Alles, was nach dem Warten erscheint, kann durch einen Rückruf als nicht blockierender Operation betrachtet werden.

Vollständige Offenlegung: Der obige Code dient nur für Demonstrationszwecke. Im tatsächlichen Code empfehle ich die Verwendung von FS. ReadFile, was einen Rückruf auslöst, der um das Versprechen eingewickelt werden kann. Die Gesamtabsicht bleibt gültig, da dies die E/A -Entfernung aus der Hauptschleife blockiert.

gehen Sie noch einen Schritt weiter

Was ist, wenn ich Ihnen gesagt habe, dass Ereignisschleifen nicht nur Stapel und Rückrufwarteschlangen aufrufen? Was ist, wenn eine Ereignisschleife nicht nur eine Schleife, sondern mehrere Schleifen ist? Was wäre, wenn es unten mehrere Threads haben könnte?

Jetzt möchte ich Sie tiefer in node.js.

Ereignisschleife Stufe

Dies sind die Ereignisschleifphasen:

The Node.js Event Loop: A Developer's Guide to Concepts & Code

Bildquelle: Libuv -Dokument

  1. Timestamp aktualisieren. Die Ereignisschleife schneidet die aktuelle Zeit zu Beginn der Schleife aus, um häufige zeitbezogene Systemaufrufe zu vermeiden. Diese Systemaufrufe sind interne Anrufe nach Libuv.
  2. Ist die Schleife aktiv? Wenn die Schleife über einen aktiven Griff, eine aktive Anfrage oder einen geschlossenen Griff verfügt, ist er aktiv. Wie gezeigt, hält der anhängige Rückruf in der Warteschlange die Schleife aktiv.
  3. Ausführen von Ablauf -Timer. Hier wird der SetTimeout- oder SetInterval -Rückruf ausgeführt. Schleifen zum Überprüfen von zwischengespeicherten nun , um abgelaufene aktive Rückrufe zum Ausführen zu ermöglichen.
  4. Führen Sie anhängige Rückrufe in der Warteschlange aus. Wenn Rückrufe durch frühere Iterationen verzögert wurden, werden diese Rückrufe zu diesem Zeitpunkt ausgeführt. Die Umfragen werden normalerweise den E/A -Rückruf sofort mit Ausnahmen ausführen. Dieser Schritt behandelt alle zurückgebliebenen Rückrufe aus der letzten Iteration.
  5. Durchführen von Leerlaufhandlern - maßgeblich wegen unsachgemäßer Benennung, weil diese Handler in jeder Iteration laufen und interne Handler für Libuv sind.
  6. Bereiten Sie sich darauf vor, den Handle auf den SetMediate -Rückruf in der Schleifen -Iteration auszuführen. Diese Griffe laufen vor den Schleifenblöcken E/O und bereiten eine Warteschlange für diesen Rückruftyp vor.
  7. Zeitüberschreitungsfrist berechnen. Die Schleife muss wissen, wann sie i/o blockiert. So berechnet es das Timeout:
    • Wenn die Schleife im Begriff steht, ist die Zeitüberschreitung 0.
    • Wenn es keinen aktiven Handle oder keine aktive Anfrage gibt, beträgt das Timeout 0.
    • Wenn es freie Griffe gibt, beträgt die Zeitüberschreitung 0.
    • Wenn in der Warteschlange anhängige Griffe vorhanden sind, beträgt die Zeitüberschreitung 0.
    • Wenn es irgendwelche Griffe gibt, die geschlossen werden, beträgt die Zeitüberschreitung 0.
    • Wenn keines der oben genannten ist, ist das Timeout auf den nächstgelegenen Timer eingestellt, und wenn es keine aktiven Timer gibt, ist es unendlich .
  8. Fahren Sie die Dauer der vorherigen Bühnenblöcke i/o. I/O-bezogene Rückrufe in der Warteschlange werden hier ausgeführt.
  9. Führen Sie den Rückruf des Handels aus. In dieser Phase ist die Stufe des SetImmediat -Laufens, der entsprechenden Phase für die Vorbereitung des Griffs. Jede SetImmediate -Rückruf in der I/A -Rückrufausführung wird hier ausgeführt.
  10. Führen Sie den geschlossenen Rückruf aus. Dies sind die aktiven Griffe, die aus der geschlossenen Verbindung freigesetzt werden.
  11. Iteration endet.

Sie fragen sich vielleicht, warum die Wahlblockierung von I/O blockiert, wenn es nicht blockiert werden soll. Die Schleife blockiert nur, wenn in der Warteschlange keine anhängigen Rückrufe vorhanden sind und der Anrufstapel leer ist. In node.js kann der nächstgelegene Timer beispielsweise über SetTimeout festgelegt werden. Wenn die Schleife auf unendlich eingestellt ist, wartet er darauf, dass eingehende Verbindungen mehr Arbeit leisten. Dies ist eine halbinfinite Schleife, da die Umfrage die Schleife aktiviert, wenn keine verbleibende Arbeit und eine aktive Verbindung besteht.

Folgendes ist die UNIX -Version dieser Timeout -Berechnung in seinem gesamten C -Codeformular:

setTimeout(
  () => console.log('Hi from the callback queue'),
  5000); // 保持循环活动这么长时间

const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Sie sind mit C vielleicht nicht sehr vertraut, aber dies liest sich wie Englisch und tut genau wie in Phase 7 beschrieben.

phasenweise Demonstration

, um jede Stufe mit reinem JavaScript anzuzeigen:

const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
// 这需要 7 秒才能执行
setTimeout(() => console.log('Ran callback A'), 5000);
Nach dem Login kopieren
Nach dem Login kopieren

Da die Datei -E/A -Rückruf vor Stufe 4 und 9 ausgeführt wird, wird erwartet, dass SetImmediate () zuerst feuert:

const fs = require('fs');
const readFileSync = async (path) => await fs.readFileSync(path);

readFileSync('readme.md').then((data) => console.log(data));
console.log('The event loop continues without blocking...');
Nach dem Login kopieren
Nach dem Login kopieren

Netzwerk -E/A ohne DNS -Abfragen ist günstiger als die Datei -E/A, da es in der Hauptereignisschleife ausgeführt wird. Die Datei -E/A wird über den Thread -Pool in der Warteschlange gestellt. DNS -Abfragen verwenden auch Threadpools. Dadurch wird das Netzwerk -E/A so teuer wie die Datei E/A.

Thread Pool

node.js hat zwei Hauptteile: die V8 JavaScript -Engine und Libuv. Datei E/O, DNS -Abfrage und Netzwerk -E/A werden über Libuv durchgeführt.

Dies ist die Gesamtstruktur:

The Node.js Event Loop: A Developer's Guide to Concepts & Code

Bildquelle: Libuv -Dokument

Für die Network I/A, Event Loops -Umfrage innerhalb des Haupt -Threads. Dieser Thread ist nicht mit Thread-Sicherheit, da er keine Kontextschalter mit einem anderen Thread hat. Die Abfragen von Datei-E/A und DNS sind plattformspezifisch. Daher besteht die Methode darin, sie in einem Thread-Pool auszuführen. Eine Idee besteht darin, DNS -Abfragen selbst durchzuführen, um das Eingeben des Thread -Pools zu vermeiden, wie im obigen Code gezeigt. Wenn Sie beispielsweise eine IP -Adresse anstelle von Localhost eingeben, wird die Suche aus dem Pool entfernt. Die Anzahl der im Thread -Pool verfügbaren Threads ist begrenzt und kann über die Umgebungsvariable uv_threadpool_size eingestellt werden. Die Standard -Thread -Poolgröße beträgt ungefähr vier.

v8 wird in einer separaten Schleife ausgeführt, löscht den Anrufstapel und gibt die Kontrolle über die Ereignisschleife zurück. V8 kann mehrere Fäden für die Müllsammlung außerhalb seiner eigenen Schleife verwenden. Stellen Sie sich V8 als Engine vor, der das ursprüngliche JavaScript nimmt und es auf der Hardware ausführt.

Für gewöhnliche Programmierer bleibt JavaScript ein Single-Threaden, da keine Fadensicherheitsprobleme vorliegen. V8 und Libuv starten intern ihre eigenen Threads, um ihre eigenen Bedürfnisse zu erfüllen.

Wenn es ein Durchsatzproblem in node.js gibt, beginnen Sie mit der Hauptveranstaltung. Überprüfen Sie, wie lange es dauert, bis eine Bewerbung eine einzige Iteration abgeschlossen hat. Es sollte nicht hundert Millisekunden überschreiten. Überprüfen Sie dann den Thread -Pool -Hunger und das, was aus dem Pool vertrieben werden kann. Die Poolgröße kann auch durch Umgebungsvariablen erhöht werden. Der letzte Schritt besteht darin, das Mikrobenchmarking von JavaScript -Code in synchron ausgeführter V8 durchzuführen.

Zusammenfassung

Die Ereignisschleife iteriert weiterhin über jede Phase, da der Rückruf in der Warteschlange ist. In jeder Phase gibt es jedoch Möglichkeiten, eine andere Art von Rückruf zu stapfen.

process.nextTick () und setimmediate ()

Am Ende jeder Phase wird der Prozess. Beachten Sie, dass dieser Rückruftyp nicht Teil der Ereignisschleife ist, da er am Ende jeder Stufe ausgeführt wird. Der Rückruf von SetImmediate () ist Teil der gesamten Ereignisschleife, so dass er nicht sofort ausgeführt wird, wie der Name impliziert. Da Process.NextTick () das Verständnis des internen Mechanismus von Ereignisschleifen erfordert, empfehle ich normalerweise die Verwendung von Setimmediate ().

Mehrere Gründe, warum Sie möglicherweise einen Prozess benötigen.NextTick ():

  1. Zulassen Sie das Netzwerk -E/A -Abschluss, Fehler zu behandeln, aufzuräumen oder Anfragen wiederholen, bevor die Schleife fortgesetzt wird.
  2. Es kann erforderlich sein, den Rückruf nach der Erweiterung des Anrufstacks auszuführen, aber bevor die Schleife fortgesetzt wird.

beispielsweise möchte ein Ereignissender ein Ereignis in seinem eigenen Konstruktor auslösen. Der Anrufstack muss erweitert werden, bevor das Ereignis aufgerufen werden kann.

setTimeout(
  () => console.log('Hi from the callback queue'),
  5000); // 保持循环活动这么长时间

const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Erlauben der Call -Stack -Erweiterung verhindert Fehler wie RangeEror: Maximale Call -Stapelgröße überschritten. Eine Sache zu beachten ist sicherzustellen, dass process.nextTick () die Ereignisschleife nicht blockiert. Rekursive Rückrufanrufe in derselben Phase können zu Blockierungsproblemen führen.

Schlussfolgerung

Ereignisschleife verkörpert die Einfachheit in seiner endgültigen Komplexität. Es löst ein schwieriges Problem wie Asynchronizität, Fadensicherheit und Parallelität. Es entfernt nutzlose oder unerwünschte Teile und maximiert den Durchsatz auf effizienteste Weise. Daher können NODE.JS -Programmierer die Zeit verkürzen, um asynchrone Fehler zu verfolgen und mehr Zeit für die Bereitstellung neuer Funktionen zu verbringen.

FAQs über Node.js Event Loops

Was ist node.js Ereignisschleife? Die Ereignisschleife von Node.js ist der Kernmechanismus, mit dem Node.js nicht blockierende asynchrone Operationen ausführen kann. Es ist verantwortlich für die Behandlung von E/A-Operationen, Timern und Rückrufen in einer einsthread-ereignisorientierten Umgebung.

Wie funktioniert die Knotenereignisschleife? Die Ereignisschleife prüft kontinuierlich auf Ereignisse oder Rückrufe in der Ereigniswarteschlange und führt sie in der Reihenfolge der Addition aus. Es läuft in einer Schleife und bearbeitet Ereignisse basierend auf der Verfügbarkeit von Ereignissen, was eine asynchrone Programmierung in node.js ermöglicht.

Welche Rolle spielen Ereignisschleifen in Node.js -Anwendungen? Ereignisschleifen stehen im Mittelpunkt von Node.js, was sicherstellt, dass Anwendungen reaktionsschnell bleiben und viele gleichzeitige Verbindungen ohne mehrere Fäden verarbeiten können.

Was sind die Stufen der Ereignisschleife von Node.js? Die Ereignisschleife in node.js verfügt über mehrere Phasen, darunter Timer, ausstehende Rückrufe, Leerlauf, Umfragen, Überprüfungen und Schließen. Diese Phasen bestimmen, wie und bestellen die Ereignisse verarbeitet.

Was sind die häufigsten Ereignisstypen, die von Ereignisschleifen verarbeitet werden? Zu den allgemeinen Ereignissen gehören E/A -Operationen (z. B. das Lesen einer Datei oder die Ausgabe einer Netzwerkanforderung), Timer (z. B. SetTimeout und SetInterval) und Callback -Funktionen (z.

Knoten Wie geht es mit langjährigen Operationen in Event-Schleifen um? Langzeit-CPU-intensive Operationen können die Ereignisschleife blockieren und sollten unter Verwendung von Modulen wie Child_Process oder Worker_Threads-Module in untergeordnete Prozesse oder Arbeiter-Threads ausgeladen werden.

Was ist der Unterschied zwischen einem Anrufstapel und einer Ereignisschleife? Der Anrufstack ist eine Datenstruktur, die Funktionsaufrufe im aktuellen Ausführungskontext verfolgt, während die Ereignisschleife für die Verwaltung asynchroner und nicht blockierender Vorgänge verantwortlich ist. Sie arbeiten zusammen, weil die Event -Schleife die Ausführung von Callbacks und E/A -Operationen plant und sie dann zum Anrufstapel schieben.

Was ist die "Häkchen" in der Ereignisschleife? "Tick" bezieht sich auf eine einzige Iteration der Ereignisschleife. In jedem Tick prüft die Event Loop über ausstehende Ereignisse und führt alle zum Ausführen bereitgestellten Rückrufe aus. Ticks ist die grundlegende Arbeitseinheit in einer Node.js -Anwendung.

Das obige ist der detaillierte Inhalt vonDie Ereignisschleife von Node.js: Die Anleitung eines Entwicklers zu Konzepten & Code. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage