Heim >Web-Frontend >Front-End-Fragen und Antworten >Technische Antwort: JavaScript-Ausführungsmechanismus

Technische Antwort: JavaScript-Ausführungsmechanismus

WBOY
WBOYnach vorne
2022-01-14 17:23:171391Durchsuche

Dieser Artikel bringt Ihnen einige Probleme im Zusammenhang mit dem Ausführungsmechanismus in JavaScript, wir stoßen oft auf Situationen, in denen wir die Ausführungsreihenfolge des Codes kennen müssen.

Technische Antwort: JavaScript-Ausführungsmechanismus

Prozesse und Threads

Wir alle wissen, dass der Kern des Computers die CPU ist, die alle Rechenaufgaben übernimmt; und das Betriebssystem ist der Manager des Computers, der für die Aufgaben verantwortlich ist Planung und Ressourcenzuweisung und -verwaltung, die die gesamte Computerhardware steuern; Anwendungen sind Programme mit bestimmten Funktionen und Programme, die auf dem Betriebssystem ausgeführt werden.

Prozess

Ein Prozess ist ein dynamischer Ausführungsprozess eines Programms mit unabhängigen Funktionen für einen Datensatz. Er ist eine unabhängige Einheit für die Ressourcenzuweisung und -planung durch das Betriebssystem und der Trägerprozess für die Anwendung Laufen ist die kleinste Einheit, die Ressourcen besitzen und unabhängig laufen kann, und sie ist auch die kleinste Einheit für die Programmausführung.

Eigenschaften eines Prozesses:

  • Dynamik: Ein Prozess ist ein Ausführungsprozess eines Programms. Er ist temporär, hat einen Lebenszyklus, wird dynamisch generiert und stirbt.

  • Parallelität: Jeder Prozess kann sein gleichzeitig mit anderen Prozessen ausgeführt;

  • Unabhängigkeit: Der Prozess ist eine unabhängige Einheit des Systems zur Ressourcenzuweisung und -planung;

  • Struktur: Der Prozess besteht aus drei Teilen: Programm, Daten und Prozesssteuerungsblock.

Thread

Ein Thread ist ein einzelner sequenzieller Steuerungsprozess bei der Programmausführung. Er ist die kleinste Einheit des Programmausführungsflusses und die Grundeinheit der Prozessorplanung und -verteilung. Ein Prozess kann einen oder mehrere Threads haben, und jeder Thread teilt sich den Speicherplatz des Programms (dh den Speicherplatz des Prozesses). Ein Standard-Thread besteht aus Thread-ID, aktuellem Befehlszeiger (PC), Registern und Stapel. Der Prozess besteht aus Speicherplatz (Code, Daten, Prozessraum, geöffnete Dateien) und einem oder mehreren Threads.

Der Unterschied zwischen Prozess und Thread

  • Thread ist die kleinste Einheit der Programmausführung und Prozess ist die kleinste Einheit der Ressourcenzuweisung durch das Betriebssystem.

  • Ein Prozess besteht aus einem oder mehreren Threads und einem Thread ist der Code in einem Prozess. Verschiedene Ausführungsrouten.

  • Prozesse sind unabhängig voneinander, aber jeder Thread unter demselben Prozess teilt sich den Speicherplatz des Programms (einschließlich Codesegmente, Datensätze, Heaps usw.) und einige Prozesse -Ebenenressourcen (z. B. das Öffnen von Dateien und Signalen), Prozesse sind füreinander unsichtbar;

  • Planung und Wechsel: Der Thread-Kontextwechsel ist viel schneller als der Prozesskontextwechsel.

Warum ist JS Single-Threaded?

JavaScript wird seit seiner Geburt als Browser-Skriptsprache verwendet. Es wird hauptsächlich für die Benutzerinteraktion und den Betrieb von DOM verwendet. Dies bedeutet, dass es nur Single-Threaded sein kann, da es sonst zu sehr komplexen Synchronisierungsproblemen kommt.

Zum Beispiel: Wenn JS Multithreading ist, ein Thread ein DOM-Element ändern möchte und ein anderer Thread das DOM-Element löschen möchte, weiß der Browser nicht, auf wen er hören soll. Um Komplexität zu vermeiden, wurde JavaScript von Anfang an so konzipiert, dass es Single-Threaded ist.

Um die Rechenleistung von Multi-Core-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 Haupt-Thread gesteuert und dürfen das DOM nicht bedienen . Daher ändert dieser neue Standard nichts an der Single-Threaded-Natur von JavaScript

Browserprinzip

Als Front-End-Ingenieur müssen Sie mit Browsern vertraut sein, und Browser sind Multiprozess-Browser.

Browser-Komponenten

  • Benutzeroberfläche: enthält Adressleiste, Vorwärts/Zurück/Aktualisieren/Lesezeichen

  • Browser-Engine: überträgt Anweisungen zwischen der Benutzeroberfläche und der Rendering-Engine

  • Rendering-Engine: verwendet um den angeforderten Inhalt zu zeichnen

  • Netzwerk: wird zum Abschließen von Netzwerkaufrufen wie http-Anfragen verwendet, verfügt über eine plattformunabhängige Schnittstelle und kann auf verschiedenen Plattformen arbeiten

  • JavaScript-Interpreter: wird zum Parsen und Ausführen von JavaScript-Code verwendet

  • Benutzeroberflächen-Backend: Wird zum Zeichnen grundlegender Widgets wie Kombinationsfelder und Fenster verwendet.

  • Datenspeicher: Gehört zur Persistenzschicht und wird vom Browser gespeichert Auf der Festplatte Für verschiedene Daten, die Cookies ähneln, definiert HTML5 die Webdatenbanktechnologie, eine leichte und vollständige clientseitige Speichertechnologie.

Hinweis: Im Gegensatz zu den meisten Browsern entspricht jeder Browser von Google (Chrome) jeder Registerkarte eine Rendering-Engine-Instanz. Jede Registerkarte ist ein unabhängiger Prozess.

Welche Prozesse enthält der Browser?

  • 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 jeder Seite, das Erstellen und Zerstören anderer Prozesse

  • Zeichnen Sie die Bitmap (Bitmap) in den Speicher, der durch den Rendering-Prozess (Renderer) zur Benutzeroberfläche erhalten wurde

  • Verwaltung von Netzwerkressourcen, Downloads usw.

  • Plug-in-Prozess von Drittanbietern

    Verantwortlich für die Verwaltung von Plug-ins von Drittanbietern

    GPU-Prozess

    Verantwortlich für 3D-Rendering und Hardwarebeschleunigung (höchstens eine)

    Rendering-Prozess

    Verantwortlich für das Parsen, Ausführen und Rendern von Seitendokumenten

    Welche Threads im Rendering-Prozess enthalten sind

    GUI-Rendering-Thread

    Hauptverantwortlich für Parsen von HTML, CSS, Erstellen eines DOM-Baums, Layout, Zeichnen usw.

    Dieser Thread ist mit der JavaScript-Engine verbunden. Threads schließen sich gegenseitig aus. Wenn der Thread der JavaScript-Engine ausgeführt wird, wird der GUI-Rendering-Thread angehalten Ist inaktiv, führt der Hauptthread das GUI-Rendering aus

    Der JavaScript-Engine-Thread

    ist hauptsächlich für die Verarbeitung von JavaScript-Skripten und die Ausführung von Code verantwortlich (z. B. V8-Engine)

    Der Browser kann nur einen JS-Engine-Thread haben, der den JS ausführt Programm gleichzeitig, das heißt, JS ist Single-Threaded

    Der JS-Engine-Thread und der GUI-Rendering-Thread schließen sich gegenseitig aus, sodass die JS-Engine das Seitenrendering blockiert

    Zeitgesteuerter Trigger-Thread

    Verantwortlich für die Ausführung des Timers Funktionen (setTimeout, setInterval)

    Browser-Timing-Zähler werden von der JS-Engine nicht gezählt (da JS Single-Threaded ist, wirkt sich eine Blockierung auf die Genauigkeit des Zählers aus) ) )

    Zeit- und Trigger-Timing durch eine separate Thread (nachdem das Timing abgeschlossen ist, fügen Sie es der Ereigniswarteschlange des ereignisauslösenden Threads hinzu und warten Sie auf die Ausführung, nachdem die JS-Engine inaktiv ist. Dieser Thread ist der Timing-Trigger-Thread, auch Timer-Thread genannt.)W3C Es ist in festgelegt Der HTML-Standard besagt, dass das Zeitintervall unter 4 ms in setTimeout als 4 ms gezählt wird Das entsprechende Ereignis wird am Ende der zu verarbeitenden Warteschlange hinzugefügt und wartet auf die Verarbeitung durch die JS-Engine.

    Asynchroner Anforderungsthread Es gibt eine entsprechende Rückruffunktion. Der asynchrone Anforderungsthread generiert ein Statusänderungsereignis und stellt die entsprechende Rückruffunktion in die Warteschlange, um auf die Ausführung durch die JS-Engine zu warten -threaded, dies legt fest, dass es sich bei den Aufgaben nicht nur um synchrone Aufgaben handeln kann, sondern auch um die Ausführung synchroner Aufgaben. Daher werden JavaScript-Aufgaben im Allgemeinen in zwei Kategorien unterteilt:

    Synchronisierte Aufgaben

    Synchronische Aufgaben beziehen sich auf: Bei Aufgaben, die zur Ausführung im Hauptthread in die Warteschlange gestellt werden, kann die nächste Aufgabe erst ausgeführt werden, nachdem die vorherige Aufgabe ausgeführt wurde.

    Asynchrone Aufgaben

    Asynchrone Aufgaben beziehen sich darauf, dass sie nicht in den Hauptthread gelangen Geben Sie jedoch die Aufgabe in die „Aufgabenwarteschlange“ (Ereigniswarteschlange) ein. Erst wenn die „Aufgabenwarteschlange“ den Hauptthread benachrichtigt, dass eine asynchrone Aufgabe ausgeführt werden kann, tritt die Aufgabe zur Ausführung in den Hauptthread ein.

    Allgemeine asynchrone Aufgaben: Timer, Ajax, Ereignisbindung, Rückruffunktionen, Versprechen, asynchrones Warten usw.

    Synchronisierte und asynchrone Aufgaben treten jeweils an unterschiedlichen Ausführungsplätzen auf, indem sie synchron in den Hauptthread und asynchron in die Ereignistabelle eintreten und registrieren Sie die Funktion.

    Wenn die in der Ereignistabelle angegebenen Dinge abgeschlossen sind, wird diese Funktion in die Ereigniswarteschlange verschoben.

    Nachdem die Aufgabe im Hauptthread ausgeführt wurde, wird sie in die Ereigniswarteschlange verschoben, um die entsprechende Funktion zu lesen und zur Ausführung in den Hauptthread einzutreten.

    Der obige Vorgang wird kontinuierlich wiederholt, was oft als Ereignisschleife bezeichnet wird.

    Wir kommen nicht umhin zu fragen: Woher wissen wir, dass der Haupt-Thread-Ausführungsstapel leer ist? In der js-Engine gibt es einen Überwachungsprozess, der kontinuierlich prüft, ob der Haupt-Thread-Ausführungsstapel leer ist. Sobald er leer ist, wird er in die Ereigniswarteschlange verschoben, um zu prüfen, ob eine Funktion auf den Aufruf wartet.

    • Makroaufgaben und Mikroaufgaben

    • Neben synchronen Aufgaben und asynchronen Aufgaben im weitesten Sinne verfügt JavaScript auch über detailliertere Aufgabendefinitionen:

    • Makroaufgabe: einschließlich globalem Code, setTimeout, setInterval

    • Mikroaufgabe: new Promise().then(callback) process.nextTick()

    • Verschiedene Arten von Aufgaben werden in unterschiedliche Aufgabenwarteschlangen eingegeben:

    Die Reihenfolge der Ereignisschleife bestimmt die Ausführungsreihenfolge des JS-Codes. Nach Eingabe des Gesamtcodes (Makrotask) startet der erste Zyklus. Führen Sie dann alle Mikrotasks aus. Beginnen Sie dann erneut mit der Makroaufgabe, suchen Sie eine der auszuführenden Aufgabenwarteschlangen und führen Sie dann alle Mikroaufgaben aus. Ausführungsstapel und Aufgabenwarteschlange

    • Ausführungsstapel

      JavaScript-Code wird in einem Ausführungskontext ausgeführt. Es gibt drei Ausführungskontexte in JavaScript:

      • Globaler Ausführungskontext

      • Funktionsausführungskontext wird erstellt

      • Eval-Ausführungskontext, der von der Eval-Funktion generierte Kontext (weniger verwendet)

      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 den Code ausführt, wird ein globaler Kontext generiert und in den Stapel verschoben Immer wenn ein Funktionsaufruf auftritt, wird der Funktionsausführungskontext generiert und auf den Ausführungsstapel verschoben. Die Engine beginnt mit der Ausführung der Funktion oben im Stapel und der Ausführungskontext wird nach der Ausführung angezeigt.

      function add(){
        console.log(1)
        foo()
        console.log(3)
      }
      function foo(){
        console.log(2)
      }
      add()

      Werfen wir einen Blick auf den Ausführungsstapel des obigen Codes:

      Technische Antwort: JavaScript-Ausführungsmechanismus

      Aufgabenwarteschlange

      Wir haben bereits erwähnt, dass alle Aufgaben in JavaScript in synchrone Aufgaben und asynchrone Aufgaben, synchrone Aufgaben usw. unterteilt sind Wie der Name schon sagt, handelt es sich um eine Aufgabe, die sofort ausgeführt wird. Sie gelangt normalerweise direkt zur Ausführung in den Hauptthread. Unsere asynchrone Aufgabe tritt in die Aufgabenwarteschlange ein und wartet auf die Ausführung der Aufgabe im Hauptthread, bevor sie ausgeführt wird.

      Die Aufgabenwarteschlange ist eine Ereigniswarteschlange, die angibt, dass verwandte asynchrone Aufgaben in den Ausführungsstapel gelangen können. Der Hauptthread liest die Aufgabenwarteschlange, um die darin enthaltenen Ereignisse zu lesen.

      Queue ist eine First-In-First-Out-Datenstruktur.

      Wir haben oben erwähnt, dass asynchrone Aufgaben in Makroaufgaben und Mikroaufgaben unterteilt werden können, sodass Aufgabenwarteschlangen auch in Makroaufgabenwarteschlangen und Mikroaufgabenwarteschlangen unterteilt werden können

      • Makroaufgabenwarteschlange: Für relativ große Arbeiten gehören zu den häufigsten Aufgaben setTimeout, setInterval, Benutzerinteraktion, UI-Rendering usw.;

      • Microtask-Warteschlange: Führen Sie kleinere Aufgaben aus. Zu den häufigsten gehören Promise, Process.nextTick; Zur Ausführung werden sie in den Hauptthread gestellt, und asynchrone Aufgaben (Klickereignisse, Timer, Ajax usw.) werden im Hintergrund ausgeführt und warten auf den Abschluss von E/A-Ereignissen oder die Auslösung 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 wird von einer Rückruffunktion verarbeitet.

      Asynchrone Aufgaben werden hier in Makroaufgaben und Mikroaufgaben unterteilt, die in die Makroaufgabenwarteschlange eingegeben werden, und Mikroaufgaben, die in die Mikroaufgabenwarteschlange eingegeben werden. Die Aufgaben in der Ausführungsaufgabenwarteschlange werden speziell im Ausführungsstapel ausgeführt. Wenn alle Aufgaben im Hauptthread ausgeführt werden, werden sie alle in der Mikroaufgabenwarteschlange ausgeführt Lesen Sie die Makroaufgaben. Der obige Vorgang wird kontinuierlich wiederholt, was wir oft als Ereignisschleife (Event-Loop) bezeichnen.

      Beispiel zur Überprüfung einer Frage

      Sehen wir uns eine zu überprüfende Frage an

      (async ()=>{
          console.log(1) 
        
          setTimeout(() => {
          console.log('setTimeout1')
          }, 0);
        
          function foo (){
              return new Promise((res,rej) => {
                  console.log(2)
                  res(3)
              })
          }
        
          new Promise((resolve,reject)=>{
          console.log(4)
          resolve() 
          console.log(5)
          }).then(()=> {
          console.log('6')
          })
        
          const res = await foo();
          console.log(res);
          console.log('7')
        
          setTimeout(_ => console.log('setTimeout2'))
      })()

      Die Druckreihenfolge lautet: 1,4,5,2,6,3,7,setTimeout1,setTimeout2

      Analyse:

      Technische Antwort: JavaScript-AusführungsmechanismusDer Code Beginne von oben. Als nächstes ausführen, zuerst auf console.log(1) stoßen, 1 direkt drucken, dann auf einen Timer stoßen, der zu einer Makroaufgabe gehört, und ihn in die Makroaufgabenwarteschlange stellen

      Dann auf ein Versprechen stoßen, denn das neue Versprechen ist ein Synchrone Aufgabe, also drucken Sie 4 direkt aus. Wenn Sie auf „Resolve“ stoßen, bei dem es sich um die nachfolgende Then-Funktion handelt, stellen Sie sie in die Mikrotask-Warteschlange, drucken Sie 5

      und führen Sie dann „await foo“ aus. Es gibt ein Versprechen in der foo-Funktion, und ein neues Versprechen ist Es handelt sich um eine synchrone Aufgabe, sodass 2 direkt gedruckt wird und „Warten“ einen Versprechensrückruf zurückgibt. Die Aufgabe nach „Warten“ wird in die Mikrotask-Warteschlange gestellt

      Schließlich wird ein Timer angetroffen und in die Makrotask-Warteschlange gestellt

      Die Ausführungsstapelaufgabe ist abgeschlossen Gehen Sie zuerst zur Mikrotask-Warteschlange, um die Mikrotask-Ausführung abzurufen, und führen Sie sie zuerst aus. Drucken Sie für die erste Mikrotask 6 aus und führen Sie dann die zweite Mikrotask aus. Drucken Sie 3, 7

      Nachdem die Mikrotask ausgeführt wurde, gehen Sie zur Makrotask-Warteschlange, um sie abzurufen Makrotask-Ausführung, print setTimeout1, setTimeout2

      [Verwandte Empfehlungen:

      Javascript-Studien-Tutorial

    Das obige ist der detaillierte Inhalt vonTechnische Antwort: JavaScript-Ausführungsmechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen