Dieser Artikel teilt Ihnen die relevanten Informationen über Timer in Node.js mit. Er ist sehr umfassend und detailliert.
Implementierung des Timers in Node.js
Wie im vorherigen Blogbeitrag erwähnt, wird der Timer in Node nicht durch das Öffnen eines neuen Threads implementiert, sondern direkt im Ereignisschleife. Im Folgenden werden mehrere JavaScript-Timer-Beispiele und Node-bezogener Quellcode verwendet, um zu analysieren, wie die Timer-Funktion in Node implementiert wird.
Eigenschaften der Timer-Funktion in JavaScript
Ob Node oder Browser, es gibt zwei Timer-Funktionen, setTimeout und setInterval, und ihre Arbeitseigenschaften sind grundlegend dasselbe, daher wird im Folgenden nur der Knoten als Beispiel für die Analyse verwendet.
Wir wissen, dass sich der Timer in JavaScript nicht vom zugrunde liegenden geplanten Interrupt des Computers unterscheidet. Wenn ein Interrupt eintrifft, wird der aktuell ausgeführte Code unterbrochen und die geplante Interrupt-Verarbeitungsfunktion ausgeführt. Wenn der JavaScript-Timer abläuft und im aktuellen Ausführungsthread kein Code ausgeführt wird, wird die entsprechende Rückruffunktion ausgeführt. Wenn derzeit Code ausgeführt wird, unterbricht die JavaScript-Engine weder den aktuellen Code, um den Rückruf auszuführen start Der neue Thread führt den Rückruf aus, wird jedoch verarbeitet, nachdem der aktuelle Code ausgeführt wurde.
console.time('A') setTimeout(function () { console.timeEnd('A'); }, 100); var i = 0; for (; i < 100000; i++) { }
Wenn Sie den obigen Code ausführen, können Sie sehen, dass die endgültige Ausgabezeit nicht etwa 100 ms, sondern einige Sekunden beträgt. Dies zeigt, dass die geplante Callback-Funktion tatsächlich nicht vor Abschluss der Schleife ausgeführt wird, sondern bis zum Ende der Schleife verschoben wird. Tatsächlich können während der Ausführung des JavaScript-Codes nicht alle Ereignisse verarbeitet werden, und neue Ereignisse müssen verarbeitet werden, bis der aktuelle Code abgeschlossen ist. Aus diesem Grund reagiert der Browser nicht mehr, wenn zeitaufwändiger JavaScript-Code im Browser ausgeführt wird. Um mit dieser Situation umzugehen, können wir die Yielding Processes-Technik verwenden, um den zeitaufwändigen Code in kleine Blöcke (Chunks) aufzuteilen. Nachdem jeder Block verarbeitet wurde, wird setTimeout einmal ausgeführt und vereinbart, dass der nächste Block verarbeitet wird Wird nach kurzer Zeit verarbeitet. Während dieser Zeit kann der Browser/Knoten Ereignisse in der Warteschlange verarbeiten.
Ergänzende Informationen
Eine ausführlichere Diskussion über erweiterte Timer und Ertragsprozesse finden Sie in Kapitel 22 „Fortgeschrittene Techniken der fortgeschrittenen JavaScript-Programmierung“, dritte Ausgabe.
Timer-Implementierung in Node
libuvs Initialisierung des Typs uv_loop_t
Im vorherigen Blog-Beitrag wurde erwähnt, dass Node die uv_run-Funktion von libuv aufruft, um default_loop_ptr für das Ereignis zu starten Bei der Planung zeigt default_loop_ptr auf eine Variable default_loop_struct vom Typ uv_loop_t. Wenn der Knoten startet, ruft er uv_loop_init(&default_loop_struct) auf, um ihn zu initialisieren. Der Auszug der Funktion uv_loop_init lautet wie folgt:
int uv_loop_init(uv_loop_t* loop) { ... loop->time = 0; uv_update_time(loop); ... }
Sie können das sehen Das Zeitfeld der Schleife wird zuerst mit 0 zugewiesen und ruft dann die Funktion uv_update_time auf, die loop.time die letzte Zählzeit zuweist.
Nach Abschluss der Initialisierung hat default_loop_struct.time einen Anfangswert und zeitbezogene Vorgänge werden mit diesem Wert verglichen, um zu bestimmen, ob die entsprechende Rückruffunktion aufgerufen werden soll.
Ereignisplanungskern von libuv
Wie bereits erwähnt, ist die uv_run-Funktion der Kernbestandteil der libuv-Bibliothek zur Implementierung der Ereignisschleife. Das Folgende ist ihr Flussdiagramm:
Hier ist eine kurze Beschreibung der obigen Logik im Zusammenhang mit dem Timer:
Aktualisieren Sie das Zeitfeld der aktuellen Schleife, das das „Jetzt“ markiert. unter dem aktuellen Schleifenkonzept;
Überprüfen Sie, ob die Schleife aktiv ist, d. h., ob es irgendwelche Aufgaben (Handler/Anfragen) gibt, die in der Schleife verarbeitet werden müssen. Es ist keine Schleife erforderlich.
Überprüfen Sie den registrierten Timer. Die in angegebene Zeit liegt hinter der aktuellen Zeit. Dies zeigt an, dass der Timer abgelaufen ist. Daher wird die entsprechende Rückruffunktion ausgeführt. O-Polling (d. h. blockiert den Thread und wartet auf das Eintreten des E/A-Ereignisses). Wenn nach Ablauf eines Timers keine E/A abgeschlossen ist, wird das Warten beendet und der Rückruf des nächsten Timers ausgeführt.
(Eigentlich ist (4.) hier komplizierter und nicht nur ein einstufiger Vorgang. Diese Beschreibung dient nur dazu, keine weiteren Details einzubeziehen und sich auf die Implementierung des Timers zu konzentrieren.)
Der Knoten ruft uv_run so lange auf, bis der Die Schleife ist nicht mehr aktiv.
timer_wrap und Timer in Node
Es gibt eine TimerWrap-Klasse in Node, die als timer_wrap-Modul in Node registriert ist. NODE_MODULE_CONTEXT_AWARE_BUILTIN(timer_wrap, node::TimerWrap::Initialize)Die TimerWrap-Klasse ist im Grunde eine direkte Kapselung von uv_timer_t, und NODE_MODULE_CONTEXT_AWARE_BUILTIN ist ein Makro, das von Node zum Registrieren integrierter Module verwendet wird.
Knotenstart und globale Initialisierung
Im vorherigen Artikel wurde erwähnt, dass Node beim Start die Ausführungsumgebung LoadEnvironment(env) lädt. Ein sehr wichtiger Schritt in dieser Funktion ist das Laden von src/node.js und das Laden der angegebenen Modul. Und global initialisieren und verarbeiten. Natürlich werden auch Funktionen wie setTimeout über src/node.js an das globale Objekt gebunden.
Das Obige ist der gesamte Inhalt dieses Artikels, ich hoffe, er gefällt euch allen.
Weitere Artikel zu Timern in Node.js finden Sie auf der chinesischen PHP-Website!