Was wird Ihrer Meinung nach passieren, wenn Sie diesen Code in der Browserkonsole ausführen?
function foo() { setTimeout(foo, 0); } foo();
Und dieser?
function foo() { Promise.resolve().then(foo); } foo();
Wenn Sie wie ich eine Reihe von Artikeln über Ereignisschleife, Hauptthread, Aufgaben, Mikrotasks und mehr gelesen haben, aber Schwierigkeiten haben, die obigen Fragen zu beantworten, ist dieser Artikel für Sie.
Also fangen wir an. Der Code für jede HTML-Seite im Browser wird im
Hauptthread ausgeführt . Hauptthread ist der Hauptthread, in dem der Browser JS ausführt, neu zeichnet, Benutzeraktionen verarbeitet und vieles mehr. Im Wesentlichen ist hier die JS-Engine in den Browser integriert.
Der einfachste Weg, dies herauszufinden, ist das Diagramm:
Abbildung 1Wir sehen, dass der einzige Ort, über den Aufgaben in den Call Stack gelangen und abgeschlossen werden können, die Ereignisschleife ist. Stellen Sie sich vor, Sie wären an seiner Stelle. Und Ihre Aufgabe ist es, mit den Aufgaben Schritt zu halten. Es gibt zwei Arten von Aufgaben:
- Persönlich - Ausführung des Haupt-JavaScript-Codes auf der Website (im Folgenden wird davon ausgegangen, dass er bereits ausgeführt wurde)
- Kundenaufgaben - Rendern, Mikrotasks und Aufgaben
Höchstwahrscheinlich werden Ihre persönlichen Aufgaben priorisiert. Event Loop stimmt dem zu :) Es bleibt, die Aufgaben des Kunden zu rationalisieren.
Das erste, was mir einfällt, ist natürlich, jedem Kunden eine Priorität einzuräumen und ihn in eine Reihe zu bringen. Die zweite besteht darin, genau zu bestimmen, wie die Aufgaben von jedem Kunden verarbeitet werden - einzeln, auf einmal oder möglicherweise in Stapeln.
Schauen Sie sich dieses Diagramm an:
Abbildung 2Basierend auf diesem Schema wird die gesamte Arbeit von Event Loop erstellt.
Nachdem wir mit der Ausführung eines Skripts begonnen haben, wird die Aufgabe mit der Ausführung dieses Skripts in die Aufgabenwarteschlange gestellt. Während dieser Code ausgeführt wird, stoßen wir auf Aufgaben von verschiedenen Kunden, die in die entsprechende Warteschlange gestellt werden. Nach Abschluss der Skriptausführungsaufgabe (Aufgabe aus Aufgaben) wechselt die Ereignisschleife zu Mikroaufgaben (nach der Aufgabe aus Aufgaben übernimmt die Ereignisschleife Aufgaben aus Mikroaufgaben). Event Loop nimmt ihm Aufgaben
ab ,
bis sie enden . Dies bedeutet, dass wenn die Zeit ihrer Hinzufügung gleich der Zeit ihrer Ausführung ist, die Ereignisschleife sie endlos harkt.
Als nächstes geht er zu Render und führt Aufgaben von ihm aus. Die Aufgaben von Render werden vom Browser optimiert. Wenn Sie der Ansicht sind, dass in diesem Zyklus nichts neu gezeichnet werden muss, geht die Ereignisschleife einfach weiter. Als Nächstes übernimmt die Ereignisschleife erneut Aufgaben aus Aufgaben und fordert sie auf,
nur eine, die erste Aufgabe in der Warteschlange , an CallStack weiterzuleiten und in der Schleife weiterzugehen.
Wenn einer der Kunden keine Aufgaben hatte, geht die Ereignisschleife einfach zum nächsten. Und im Gegenteil, wenn die Aufgaben des Kunden viel Zeit in Anspruch nehmen, warten die verbleibenden Kunden darauf, dass sie an die Reihe kommen. Und wenn sich herausstellt, dass die Aufgaben eines Kunden endlos sind, läuft der Call Stack über und der Browser beginnt zu schwören:
Abbildung 3 Nachdem wir nun verstanden haben, wie Event Loop funktioniert, ist es Zeit herauszufinden, was passiert, nachdem die Codefragmente am Anfang dieses Artikels ausgeführt wurden.
function foo() { setTimeout(foo, 0); } foo();
Wir sehen, dass sich die Funktion foo über setTimeout im Inneren rekursiv aufruft, aber bei jedem Aufruf eine Kundenaufgabe für Aufgaben erstellt. Wie wir uns erinnern, wird in der Ereignisschleifenschleife beim Ausführen der Aufgabenwarteschlange aus Aufgaben nur 1 Aufgabe in der Schleife ausgeführt. Und dann gibt es Aufgaben von Microtasks und Render. Daher führt dieser Code nicht dazu, dass die Ereignisschleife leidet und ihre Aufgaben für immer ausgeführt werden. Aber es wird in jeder Runde eine neue Aufgabe für die Kundenaufgaben werfen.
Versuchen wir, dieses Skript im Google Chrome-Browser auszuführen. Zu diesem Zweck habe ich ein einfaches HTML-Dokument erstellt und script.js mit diesem Code verbunden. Gehen Sie nach dem Öffnen des Dokuments zu den Entwicklertools, öffnen Sie die Registerkarte Leistung und klicken Sie auf die Schaltfläche "Profilerstellung starten und Seite neu laden":
Abbildung 4Wir sehen, dass die Aufgaben von Aufgaben nacheinander ausgeführt werden, ungefähr alle 4 ms.
Betrachten Sie das zweite Rätsel:
function foo() { Promise.resolve().then(foo); } foo();
Hier sehen wir dasselbe wie im obigen Beispiel, aber das Aufrufen von foo fügt Aufgaben von Microtasks hinzu, und alle sind erledigt, bis sie enden. Und dies bedeutet, dass er bis zum Abschluss der Ereignisschleife nicht zum nächsten Kunden übergehen kann :( Und wir sehen wieder ein trauriges
Bild .
Schauen Sie sich dies in den Entwicklungswerkzeugen an:
Abbildung 5Wir sehen, dass Mikrotasks ungefähr alle 0,1 ms ausgeführt werden, und dies ist 40-mal schneller als die Tasks-Warteschlange. Alles nur, weil sie alle auf einmal aufgeführt werden. In unserem Beispiel wird die Warteschlange endlos verschoben. Zur Visualisierung habe ich es auf 100.000 Iterationen reduziert.
Das ist alles!
Ich hoffe, dieser Artikel hat Ihnen geholfen, und jetzt verstehen Sie, wie Event Loop funktioniert und was in den obigen Codebeispielen vor sich geht.
Tschüss :) Und bis bald. Wenn es dir gefallen hat, mag und abonniere meinen Kanal :)