Inhaltsverzeichnis- 1. Das Konzept von Prozess und Thread
- 2. Browserprinzip
- 3. Synchron vs. Asynchron
- 4. Ausführungsstapel und Aufgabenwarteschlange
- 5. Ereignisschleife
- 6. Zeitgeber
Vorwort:
Ob bei der Arbeit oder bei Vorstellungsgesprächen, wir stoßen häufig auf Szenarien, in denen wir die Ausführungsreihenfolge des Codes kennen müssen. Daher habe ich vor, etwas Zeit darauf zu verwenden, den Ausführungsmechanismus von JavaScript gründlich zu verstehen. Um den Ausführungsmechanismus von JavaScript zu verstehen, müssen Sie Folgendes wissen: (Nehmen Sie als Beispiel die Browserumgebung, die sich von der Node-Umgebung unterscheidet.) 1. Das Konzept von Prozess und Thread
- Browserprinzip
- Ereignisschleife (Event-Loop), Task-Warteschlange (synchrone Tasks, asynchrone Tasks, Mikrotasks, Makrotasks)
- Prozesse und Threads
Wir alle wissen, dass der Kern des Computers die CPU ist, die alle Rechenaufgaben übernimmt; das Betriebssystem ist der Manager des Computers, der für die Aufgabenplanung, Ressourcenzuweisung und -verwaltung verantwortlich ist und die gesamte Computerhardware steuert; die Anwendung ist ein Programm mit bestimmten Funktionen, und das Programm wird auf dem Betriebssystem ausgeführt. Verfahren:
Ein Prozess ist der dynamische Ausführungsprozess eines Programms mit unabhängigen Funktionen auf einem Datensatz. Er ist eine unabhängige Einheit für die Ressourcenzuweisung und -planung durch das Betriebssystem. Er ist der Träger für die Ausführung von Anwendungsprogrammen. Ein Prozess ist die kleinste Einheit, die Ressourcen besitzen und unabhängig ausgeführt werden kann, und er ist auch die kleinste Einheit der Programmausführung. Eigenschaften eines Prozesses: - Dynamik: Ein Prozess ist ein Ausführungsprozess eines Programms. Er ist temporär, hat einen Lebenszyklus und wird dynamisch generiert und zerstört.
- Gleichzeitigkeit: Jeder Prozess kann gleichzeitig mit anderen Prozessen ausgeführt werden.
- Unabhängigkeit: Ein Prozess ist eine unabhängige Einheit für die Ressourcenzuweisung und -planung im System.
- Strukturell: Ein Prozess besteht aus drei Teilen: Programm, Daten und Prozesskontrollblock.
Themen:
Ein Thread ist ein einzelner sequentieller Steuerungsfluss bei der Programmausführung, die kleinste Einheit des Programmausführungsflusses und die grundlegende Einheit der Prozessorplanung und -verteilung. Ein Prozess kann einen oder mehrere Threads haben, und jeder Thread teilt sich den Speicherplatz des Programms (d. h. den Speicherplatz des Prozesses). Ein Standard-Thread besteht aus einer Thread-ID, dem aktuellen Befehlszeiger (PC), Registern und einem Stapel. Ein Prozess besteht aus Speicherplatz (Code, Daten, Prozessspeicher, offene Dateien) und einem oder mehreren Threads. Der Unterschied zwischen einem Prozess und einem Thread:
- Ein Thread ist die kleinste Einheit der Programmausführung, während ein Prozess die kleinste Einheit der Ressourcenzuweisung durch das Betriebssystem ist.
- Ein Prozess besteht aus einem oder mehreren Threads, die unterschiedliche Ausführungsrouten des Codes in einem Prozess darstellen.
- Prozesse sind voneinander unabhängig, aber Threads unter demselben Prozess teilen sich den Speicherplatz des Programms (einschließlich Codesegmente, Datensätze, Heaps usw.) und einige Ressourcen auf Prozessebene (wie offene Dateien und Signale). Prozesse sind füreinander nicht sichtbar.
- Planen und Wechseln: Das Wechseln des Threadkontexts ist viel schneller als das Wechseln des Prozesskontexts.
Warum ist JS Single-Threaded?
JavaScript wird seit seiner Einführung als Skriptsprache für Browser verwendet und dient hauptsächlich zur Handhabung von Benutzerinteraktionen und zum Betreiben von DOM. Dies bestimmt, dass es nur Single-Threaded sein kann, da es sonst zu sehr komplexen Synchronisierungsproblemen kommt. Beispiel: Wenn JS mehrere Threads hat, möchte ein Thread ein DOM-Element ändern und ein anderer Thread möchte das DOM-Element löschen. Zu diesem Zeitpunkt weiß der Browser nicht, auf wen er hören soll. Um Komplexität zu vermeiden, wurde JavaScript von Anfang an als Single-Thread-Programm konzipiert. Um die Rechenleistung von Mehrkern-CPUs zu nutzen, schlägt HTML5 den Web Worker-Standard vor, der es JavaScript-Skripten ermöglicht, mehrere Threads zu erstellen. Die untergeordneten Threads werden jedoch vollständig vom Hauptthread gesteuert und dürfen den DOM nicht bedienen. Daher ändert dieser neue Standard nichts an der Single-Thread-Natur von JavaScript. 2. Browserprinzip
Als Front-End-Entwickler müssen Sie mit Browsern vertraut sein, und Browser sind Multiprozess-Browser. Browserkomponenten:
- Benutzeroberfläche : einschließlich Adressleiste, Vorwärts/Zurück/Aktualisieren/Lesezeichen usw.
- Browser-Engine : überträgt Anweisungen zwischen der Benutzeroberfläche und der Rendering-Engine
- Rendering-Engine : wird zum Zeichnen des angeforderten Inhalts verwendet
- Netzwerk : Wird zum Abschließen von Netzwerkaufrufen wie HTTP-Anfragen verwendet. Es verfügt über eine plattformunabhängige Schnittstelle und kann auf verschiedenen Plattformen ausgeführt werden.
- JavaScript-Interpreter : wird zum Parsen und Ausführen von
JavaScript Code verwendet - Benutzeroberfläche Backend : Wird zum Zeichnen grundlegender Widgets wie Kombinationsfelder und Fenster verwendet und verwendet die Benutzeroberfläche des Betriebssystems unten
- Datenspeicherung : gehört zur Persistenzschicht. Der Browser speichert verschiedene Daten ähnlich wie
cookie auf der Festplatte. HTML5 definiert die Webdatenbanktechnologie, eine leichte und vollständige Client-Speichertechnologie.
⚠️Hinweis : Anders als die meisten Browser verfügt Chrome über eine separate Rendering-Engine-Instanz pro Tab. Jede Registerkarte ist ein separater Prozess
Aus welchen Prozessen besteht ein Browser?
Browserprozess: - Der Hauptprozess des Browsers (verantwortlich für Koordination und Kontrolle) gibt es nur einen
- Verantwortlich für die Anzeige der Browseroberfläche und die Interaktion mit Benutzern. Wie vorwärts, rückwärts usw.
- Verantwortlich für die Verwaltung verschiedener Seiten, das Erstellen und Löschen anderer Prozesse
- Zeichnen Sie das vom
Renderer -Prozess im Speicher erhaltene Bitmap auf die Benutzeroberfläche - Verwaltung von Netzwerkressourcen, Downloads usw.
Prozess für Drittanbieter-Plugins: - Verantwortlich für die Verwaltung von Plug-Ins von Drittanbietern
GPU-Prozess: - Verantwortlich für 3D-Zeichnen und Hardwarebeschleunigung (maximal eine)
Rendering-Prozess : - Verantwortlich für das Parsen, Ausführen und Rendern von Seitendokumenten
Welche Threads enthält der Rendering-Prozess?
GUI-Rendering-Thread:
- Hauptsächlich verantwortlich für das Parsen von HTML, CSS, Erstellen des DOM-Baums, Layout, Zeichnen usw.
- Dieser Thread und der Thread der JavaScript-Engine schließen sich gegenseitig aus. Wenn
JavaScript Engine ausgeführt wird, wird der Thread für das GUI-Rendering angehalten. Wenn die Aufgabenwarteschlange im Leerlauf ist, führt der Hauptthread das GUI-Rendering aus.
Thread der JavaScript-Engine:
- Hauptverantwortlich für die Verarbeitung
JavaScript -Skripten und die Ausführung von Codes (z. B. V8-Engine) - Der Browser kann nur über einen JS-Engine-Thread verfügen, der das JS-Programm gleichzeitig ausführt. Das heißt, JS ist ein Single-Thread.
- Der JS-Engine-Thread und der GUI-Rendering-Thread schließen sich gegenseitig aus, sodass die JS-Engine das Seiten-Rendering blockiert
Thread zum zeitlichen Auslösen:
- Verantwortlich für die Ausführung von Timerfunktionen (
setTimeout,setInterval ) - Der Zeitzähler des Browsers wird von der JS-Engine nicht gezählt (da JS ein Single-Thread ist, beeinträchtigt ein blockierter Zustand die Genauigkeit des Zählers).
- Ein separater Thread wird zum Timing und Auslösen des Timings verwendet (nach Abschluss des Timings wird er zur Ereigniswarteschlange des Ereignisauslösethreads hinzugefügt und wartet, bis die JS-Engine im Leerlauf ist, bevor er ausgeführt wird). Dieser Thread ist der Timing-Auslösethread, auch Timer-Thread genannt.
- W3C legt im HTML-Standard fest, dass das Zeitintervall von weniger als 4 ms in
setTimeout als 4 ms gezählt wird.
Ereignisauslösender Thread:
- Verantwortlich für die Übergabe der vorbereiteten Ereignisse an den JS-Engine-Thread zur Ausführung
- Wenn ein Ereignis ausgelöst wird, fügt der Thread das entsprechende Ereignis zur Verarbeitung an das Ende der Warteschlange hinzu und wartet darauf, dass die JS-Engine es verarbeitet.
Asynchroner Anforderungsthread:
- Nach der
XMLHttpRequest -Verbindung öffnet der Browser einen Thread - Wenn beim Erkennen einer Änderung des Anforderungsstatus eine entsprechende Rückruffunktion vorhanden ist, generiert der asynchrone Anforderungsthread ein Statusänderungsereignis und stellt die entsprechende Rückruffunktion in die Warteschlange, um auf die Ausführung durch die JS-Engine zu warten.
3. Synchron vs. Asynchron
Da JavaScript ein Single-Thread ist, können seine Aufgaben nicht nur synchrone Aufgaben sein. Wenn Aufgaben, die lange dauern, auch als synchrone Aufgaben ausgeführt werden, wird die Seite blockiert. Daher werden JavaScript-Aufgaben im Allgemeinen in zwei Kategorien unterteilt: Synchrone Aufgaben:
Synchrone Aufgaben beziehen sich auf Aufgaben, die zur Ausführung im Hauptthread in die Warteschlange gestellt werden. Die nächste Aufgabe kann erst ausgeführt werden, nachdem die vorherige Aufgabe abgeschlossen ist. Asynchrone Aufgaben:
Eine asynchrone Aufgabe bezieht sich auf eine Aufgabe, die nicht in den Hauptthread gelangt, sondern in die „Aufgabenwarteschlange“ (Ereigniswarteschlange). Erst wenn die „Aufgabenwarteschlange“ den Hauptthread benachrichtigt, dass eine asynchrone Aufgabe ausgeführt werden kann, gelangt die Aufgabe zur Ausführung in den Hauptthread. Häufige asynchrone Aufgaben: Timer, Ajax, Ereignisbindung, Rückruffunktionen, promise , async await usw. - Synchrone und asynchrone Tasks gelangen jeweils an unterschiedliche Ausführungsorte. Synchrone Tasks gelangen in den Hauptthread, während asynchrone Tasks in die Ereignistabelle und Registerfunktionen gelangen.
- Wenn die in der Ereignistabelle angegebenen Dinge abgeschlossen sind, wird diese Funktion in die Ereigniswarteschlange verschoben.
- Wenn die Aufgabe im Hauptthread abgeschlossen und leer ist, wird sie zur
Event Queue weitergeleitet, um die entsprechende Funktion zu lesen und zur Ausführung in den Hauptthread einzugeben. - Der oben beschriebene Vorgang wird kontinuierlich wiederholt, was oft als Ereignisschleife bezeichnet wird.
- Wir können nicht anders, als zu fragen: Woher wissen wir, dass der Ausführungsstapel des Hauptthreads leer ist? Die JS-Engine verfügt über einen
monitoring process , der kontinuierlich prüft, ob der Ausführungsstapel des Hauptthreads leer ist. Sobald er leer ist, wird in Event Queue geprüft, ob Funktionen darauf warten, aufgerufen zu werden.
Makroaufgaben und Mikroaufgaben:
Zusätzlich zu den allgemeinen synchronen und asynchronen Aufgaben verfügt JavaScript auch über komplexere Aufgabendefinitionen: - Makro-Aufgabe: einschließlich globalem Code, setTimeout, setInterval
- Mikroaufgabe: neues Promise().then(callback) process.nextTick()
Verschiedene Aufgabentypen gelangen in unterschiedliche Aufgabenwarteschlangen: 
Die Reihenfolge der Ereignisschleife bestimmt die Ausführungsreihenfolge des JS-Codes. Nach Eingabe des Gesamtcodes (Makrotask) beginnt die erste Schleife. Führen Sie dann alle Mikroaufgaben aus. Beginnen Sie dann erneut mit der Makroaufgabe, suchen Sie eine der Aufgabenwarteschlangen zum Abschließen und führen Sie dann alle Mikroaufgaben aus. 4. Ausführungsstapel und Aufgabenwarteschlange
Ausführungsstapel:
JavaScript-Code wird in einem Ausführungskontext ausgeführt. In JavaScript gibt es drei Ausführungskontexte: - Globaler Ausführungskontext
- Funktionsausführungskontext, ein Funktionsausführungskontext wird erstellt, wenn eine JS-Funktion aufgerufen wird
- eval-Ausführungskontext, der von der eval-Funktion generierte Kontext (weniger gebräuchlich)
Im Allgemeinen hat unser JS-Code mehr als einen Kontext. Wie ist also die Ausführungsreihenfolge dieser Kontexte? Wir alle wissen, dass der Stapel eine Last-In-First-Out-Datenstruktur ist. Der Ausführungsstapel in unserem JavaScript ist eine solche Stapelstruktur. Wenn die JS-Engine Code ausführt, wird ein globaler Kontext generiert und in den Ausführungsstapel geschoben. Immer wenn ein Funktionsaufruf erfolgt, wird ein Funktionsausführungskontext generiert und in den Ausführungsstapel geschoben. Die Engine beginnt mit der Ausführung der Funktion oben auf dem Stapel und ruft nach der Ausführung den Ausführungskontext ab.
Funktion add(){
console.log(1)
foo()
console.log(3)
}
Funktion foo(){
console.log(2)
}
hinzufügen()
Werfen wir einen Blick auf den Ausführungsstapel des obigen Codes: 
Aufgabenwarteschlange:
Wir haben bereits erwähnt, dass alle Aufgaben in JavaScript in synchrone und asynchrone Aufgaben unterteilt sind. Synchrone Aufgaben sind, wie der Name schon sagt, Aufgaben, die sofort ausgeführt werden. Sie gelangen im Allgemeinen direkt zur Ausführung in den Hauptthread. Unsere asynchronen Aufgaben gelangen in die Aufgabenwarteschlange und warten vor der Ausführung, bis die Aufgaben im Hauptthread abgeschlossen sind. Die Aufgabenwarteschlange ist eine Warteschlange von Ereignissen, die angibt, dass zugehörige asynchrone Aufgaben in den Ausführungsstapel gelangen können. Der Hauptthread liest die Aufgabenwarteschlange, um zu erfahren, welche Ereignisse sich darin befinden. Eine Warteschlange ist eine First-In-First-Out-Datenstruktur. Wie oben erwähnt, können asynchrone Aufgaben in Makrotasks und Mikrotasks unterteilt werden, sodass Aufgabenwarteschlangen auch in Makrotask-Warteschlangen und Mikrotask-Warteschlangen unterteilt werden können. -
Macrotask Queue : führt relativ große Aufgaben aus, einschließlich setTimeout , setInterval , Benutzerinteraktion, UI-Rendering usw. -
Microtask Queue : führt kleinere Aufgaben aus, häufig verwendet werden Promise und Process.nextTick ;
5. Ereignisschleife
- Synchrone Aufgaben werden zur Ausführung direkt in den Hauptthread eingefügt, und asynchrone Aufgaben (Klickereignisse, Timer, Ajax usw.) werden im Hintergrund ausgeführt und warten auf den Abschluss von E/A-Ereignissen oder das Auslösen von Verhaltensereignissen.
- Das System führt asynchrone Aufgaben im Hintergrund aus. Wenn ein asynchrones Aufgabenereignis (oder Verhaltensereignis) ausgelöst wird, wird die Aufgabe zur Aufgabenwarteschlange hinzugefügt und jede Aufgabe entspricht einer Rückruffunktion zur Verarbeitung.
- Dabei werden asynchrone Tasks in Makrotasks und Mikrotasks unterteilt. Makrotasks gelangen in die Makrotask-Warteschlange und Mikrotasks in die Mikrotask-Warteschlange.
- Die Aufgaben in der Ausführungsaufgabenwarteschlange werden im Ausführungsstapel abgeschlossen. Wenn alle Aufgaben im Hauptthread ausgeführt sind, wird die Mikrotaskwarteschlange gelesen. Wenn Mikrotasks vorhanden sind, werden sie alle ausgeführt und dann wird die Makrotaskwarteschlange gelesen.
- Der obige Vorgang wird kontinuierlich wiederholt, was wir oft als
Event-Loop ) bezeichnen.

Beispielüberprüfung:
Schauen wir uns eine Frage an, um das zu überprüfen
(asynchron () => {
console.log(1)
setzeTimeout(() => {
console.log('Zeitüberschreitung1 festlegen')
}, 0);
Funktion foo () {
gib ein neues Promise zurück((res,rej) => {
console.log(2)
Ergebnis(3)
})
}
neues Versprechen((lösen,ablehnen)=>{
console.log(4)
lösen()
console.log(5)
}).dann(()=> {
konsole.log('6')
})
const res = warte auf foo();
konsole.log(res);
konsole.log('7')
setTimeout(_ => console.log('setTimeout2'))
})()
Die Druckreihenfolge ist: 1,4,5,2,6,3,7,setTimeout1,setTimeout2
analysieren: - Der Code wird von oben nach unten ausgeführt. Zuerst stößt er auf
console.log(1) , das direkt 1 ausgibt. Dann stößt er auf den Timer, der zur Makroaufgabe gehört, und stellt ihn in die Warteschlange der Makroaufgabe. - Wenn wir erneut auf Promise stoßen, drucken wir direkt 4, da es sich bei
new Promise um eine synchrone Aufgabe handelt. Wenn wir auf Resolve stoßen, was die nachfolgende then Funktion ist, stellen wir es in die Microtask-Warteschlange und drucken 5. - Führen Sie dann „await foo“ aus. In der Funktion foo gibt es ein
promise . new promise gehört zu synchronen Tasks, daher wird 2 direkt ausgegeben. Was „await“ zurückgibt, ist ein promise -Callback. Die Tasks nach „await“ werden in die Microtask-Warteschlange gestellt. - Schließlich wird ein Timer gefunden und in die Makro-Task-Warteschlange gestellt
- Nachdem die Ausführungsstapelaufgabe ausgeführt wurde, gehen Sie zur Mikrotask-Warteschlange, um die Mikrotask-Ausführung abzurufen. Führen Sie zuerst die erste Mikrotask aus, drucken Sie 6, führen Sie dann die zweite Mikrotask aus, drucken Sie 3, 7
- Nachdem die Mikrotask ausgeführt wurde, gehen Sie zur Makrotask-Warteschlange, um die Ausführung der Makrotask abzurufen und
setTimeout1 und setTimeout2 auszudrucken.
6. Zeitgeber
Asynchrone Aufgaben in der Aufgabenwarteschlange von JavaScript enthalten auch Timerereignisse, die angeben, wie lange die Ausführung bestimmter Codes dauert. Die Timerfunktion wird hauptsächlich durch zwei Funktionen vervollständigt, setTimeout() und nterval() . Ihre internen Ausführungsmechanismen sind genau gleich. Der Hauptunterschied besteht darin, dass setTimeout ein einmaliger Ausführungsprozess ist, während setInterval ein wiederholter Ausführungsprozess ist. Die Funktion setTimeout akzeptiert zwei Parameter, der erste ist die auszuführende Rückruffunktion und der zweite ist die Zeit (ms), um die die Ausführung verschoben werden soll. Wenn wir die Verzögerungszeit auf 0 setzen, wird es dann sofort ausgeführt?
setzeTimeout(()=>{
console.log(1)
},0)
console.log(2)
Dies ist jedoch nicht der Fall. Das obige Druckergebnis ist, dass zuerst 2 und dann 1 gedruckt wird. Fühlen Sie sich verwirrt? Es ist sehr einfach, dies anhand der Regeln der obigen Ereignisschleife zu verstehen. Der globale Code wird ausgeführt und wenn der Timer setTimeout erreicht wird, wird er in die Warteschlange der Makroaufgabe gestellt. Anschließend wird der Synchronisierungscode nach unten ausgeführt und 2 gedruckt. Nachdem die Stapelaufgabe ausgeführt wurde, geht sie in die Warteschlange der Mikroaufgabe. Wenn keine Mikroaufgabe vorhanden ist, sehen Sie sich die Warteschlange der Makroaufgabe erneut an. Es gibt eine Makroaufgabe und print 1 wird ausgeführt. Die Bedeutung von setTimeout(fn,0) besteht darin, anzugeben, dass eine Aufgabe zum frühestmöglichen Leerlaufzeitpunkt des Hauptthreads ausgeführt wird, d. h., sie so früh wie möglich auszuführen. Es fügt ein Ereignis am Ende der „Aufgabenwarteschlange“ hinzu, sodass es erst ausgeführt wird, wenn die Synchronisierungsaufgabe und die vorhandenen Ereignisse in der „Aufgabenwarteschlange“ verarbeitet wurden. Der HTML5-Standard legt fest, dass der Mindestwert (kürzestes Intervall) des zweiten Parameters von setTimeout() nicht weniger als 4 Millisekunden betragen darf. Wenn er unter diesem Wert liegt, wird er automatisch erhöht. Zuvor war bei älteren Browserversionen das Mindestintervall auf 10 Millisekunden festgelegt. Darüber hinaus werden DOM-Änderungen (insbesondere solche, die eine erneute Seitendarstellung beinhalten) normalerweise nicht sofort, sondern alle 16 Millisekunden ausgeführt. Derzeit ist requestAnimationFrame() besser als setTimeout(). Es ist zu beachten, dass setTimeout() das Ereignis nur in die „Aufgabenwarteschlange“ einfügt. Der Hauptthread führt die angegebene Rückruffunktion erst aus, wenn der aktuelle Code (Ausführungsstapel) ausgeführt wird. Wenn der aktuelle Code lange dauert, müssen Sie möglicherweise lange warten. Es gibt daher keine Garantie dafür, dass die Rückruffunktion zum durch setTimeout() angegebenen Zeitpunkt ausgeführt wird. Dies ist das Ende dieses Artikels über die detaillierte Einführung des JavaScript-Ausführungsmechanismus. Weitere relevante Inhalte zum JavaScript-Ausführungsmechanismus finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen! Das könnte Sie auch interessieren:- Tiefgreifendes Verständnis des Ereignisausführungsmechanismus von JavaScript
- Verwenden Sie einige Interviewfragen, um den Ausführungsmechanismus von JavaScript zu untersuchen
- Detaillierte Erklärung des JavaScript-Ausführungsmechanismus
- Den Ausführungsmechanismus von JavaScript genau verstehen
|