asynchrones Programmieren von Node.js: eingehendes Verständnis von Ereignisschleifen
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.
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.
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) {}
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.
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);
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
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...');
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
Jetzt möchte ich Sie tiefer in node.js.
Dies sind die Ereignisschleifphasen:
Bildquelle: Libuv -Dokument
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) {}
Sie sind mit C vielleicht nicht sehr vertraut, aber dies liest sich wie Englisch und tut genau wie in Phase 7 beschrieben.
, um jede Stufe mit reinem JavaScript anzuzeigen:
const stopTime = Date.now() + 2000; while (Date.now() < stopTime) {} // 这需要 7 秒才能执行 setTimeout(() => console.log('Ran callback A'), 5000);
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...');
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.
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:
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.
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.
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 ():
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) {}
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.
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.
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!