[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/de/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth [Back]  [Original]

Im Detail: Microtasks und die JavaScript-Laufzeitumgebung - Web-APIs | MDN

Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.

View in English Always switch to English

Im Detail: Microtasks und die JavaScript-Laufzeitumgebung

Beim Debuggen oder mglicherweise bei der Entscheidung ber den besten Ansatz zur Lsung eines Problems rund um das Timing und die Planung von Aufgaben und Microtasks kann es ntzlich sein, einige Details darber zu verstehen, wie die JavaScript-Laufzeit unter der Haube funktioniert.

JavaScript ist eine von Natur aus einzelfdige Sprache. Sie wurde in einer Zeit entwickelt, in der dies eine positive Entscheidung war; es gab nur wenige Mehrprozessor-Computer fr die breite ffentlichkeit und die erwartete Menge an Code, die von JavaScript verarbeitet werden wrde, war zu dieser Zeit relativ gering.

Im Laufe der Zeit haben sich Computer natrlich zu leistungsstarken Mehrkernsystemen entwickelt, und JavaScript ist eine der meistgenutzten Sprachen in der Computerwelt geworden. Eine Vielzahl der beliebtesten Anwendungen basiert zumindest teilweise auf JavaScript-Code. Um dies zu untersttzen, war es notwendig, Wege zu finden, um Projekte aus den Einschrnkungen einer einzelfdigen Sprache zu befreien.

Beginnend mit der Einfhrung von Timeouts und Intervallen als Teil der Web-API (setTimeout() und setInterval()) hat die von Webbrowsern bereitgestellte JavaScript-Umgebung allmhlich fortgeschrittene Funktionen entwickelt, die die Planung von Aufgaben, die Entwicklung mehrfdiger Anwendungen und so weiter ermglichen. Um zu verstehen, wo queueMicrotask() hier ins Spiel kommt, ist es hilfreich zu verstehen, wie die JavaScript-Laufzeit beim Planen und Ausfhren von Code operiert.

In diesem Artikel

JavaScript-Ausfhrungskontexte

Hinweis: Die hier beschriebenen Details sind fr die meisten JavaScript-Programmierer im Allgemeinen nicht wichtig. Diese Informationen werden als Grundlage dafr bereitgestellt, warum Microtasks ntzlich sind und wie sie funktionieren; wenn es Sie nicht interessiert, knnen Sie diesen Abschnitt berspringen und spter zurckkommen, wenn Sie feststellen, dass Sie es brauchen.

Wenn ein Fragment von JavaScript-Code ausgefhrt wird, luft es in einem Ausfhrungskontext. Es gibt drei Arten von Code, die einen neuen Ausfhrungskontext erstellen:

  • Der globale Kontext ist der Ausfhrungskontext, der erstellt wird, um den Hauptteil Ihres Codes auszufhren; das heit, jeder Code, der auerhalb einer JavaScript-Funktion existiert.
  • Jede Funktion wird in ihrem eigenen Ausfhrungskontext ausgefhrt. Dieser wird hufig als "lokaler Kontext" bezeichnet.
  • Die Verwendung der nicht empfohlenen eval() Funktion erstellt ebenfalls einen neuen Ausfhrungskontext.

Jeder Kontext ist im Wesentlichen ein Geltungsbereich innerhalb Ihres Codes. Wenn eines dieser Code-Segmente mit der Ausfhrung beginnt, wird ein neuer Kontext erstellt, in dem es ausgefhrt wird; dieser Kontext wird dann beim Verlassen des Codes zerstrt. Betrachten Sie das folgende JavaScript-Programm:

js
const outputElem = document.getElementById("output");

const userLanguages = {
  Mike: "en",
  Teresa: "es",
};

function greetUser(user) {
  function localGreeting(user) {
    let greeting;
    const language = userLanguages[user];

    switch (language) {
      case "es":
        greeting = `Hola, ${user}!`;
        break;
      case "en":
      default:
        greeting = `Hello, ${user}!`;
        break;
    }
    return greeting;
  }
  outputElem.innerText += `${localGreeting(user)}\n`;
}

greetUser("Mike");
greetUser("Teresa");
greetUser("Veronica");

Dieses kurze Programm enthlt drei Ausfhrungskontexte, von denen einige im Laufe der Programmausfhrung mehrmals erstellt und zerstrt werden. Jeder Kontext wird beim Erstellen auf den Ausfhrungskontext-Stack gelegt. Beim Beenden wird der Kontext von diesem Stack entfernt.

  • Beim Start des Programms wird der globale Kontext erstellt.

    • Wenn greetUser("Mike") erreicht wird, wird ein Kontext fr die greetUser()-Funktion erstellt; dieser Ausfhrungskontext wird auf den Ausfhrungskontext-Stack geschoben.

      • Wenn greetUser() die Funktion localGreeting() aufruft, wird ein weiterer Kontext erstellt, um diese Funktion auszufhren. Wenn diese Funktion zurckkehrt, wird der Kontext fr localGreeting() aus dem Ausfhrungsstack entfernt und zerstrt. Die Programmausfhrung wird mit dem nchsten Kontext im Stack fortgesetzt, was greetUser() ist; diese Funktion wird an der Stelle fortgesetzt, an der sie unterbrochen wurde.
      • Die Funktion greetUser() kehrt zurck und ihr Kontext wird aus dem Stack entfernt und zerstrt.
    • Wenn greetUser("Teresa") erreicht wird, wird ein Kontext dafr erstellt und auf den Stack geschoben.

      • Wenn greetUser() die Funktion localGreeting() aufruft, wird ein weiterer Kontext erstellt, um diese Funktion auszufhren. Wenn diese Funktion zurckkehrt, wird der Kontext fr localGreeting() aus dem Ausfhrungsstack entfernt und zerstrt. greetUser() wird an der Stelle fortgesetzt, an der es unterbrochen wurde.
      • Die Funktion greetUser() kehrt zurck und ihr Kontext wird aus dem Stack entfernt und zerstrt.
    • Wenn greetUser("Veronica") erreicht wird, wird ein Kontext dafr erstellt und auf den Stack geschoben.

      • Wenn greetUser() die Funktion localGreeting() aufruft, wird ein weiterer Kontext erstellt, um diese Funktion auszufhren. Wenn diese Funktion zurckkehrt, wird der Kontext fr localGreeting() aus dem Ausfhrungsstack entfernt und zerstrt.
      • Die Funktion greetUser() kehrt zurck und ihr Kontext wird aus dem Stack entfernt und zerstrt.
  • Das Hauptprogramm beendet sich, und sein Ausfhrungskontext wird aus dem Ausfhrungsstack entfernt; da keine Kontexte mehr im Stack vorhanden sind, endet die Programmausfhrung.

Durch die Verwendung von Ausfhrungskontexten auf diese Weise knnen jedes Programm und jede Funktion ihre eigene Menge an Variablen und anderen Objekten haben. Jeder Kontext verfolgt zudem die nchste Zeile im Programm, die ausgefhrt werden soll, und andere Informationen, die fr den Betrieb dieses Kontexts entscheidend sind. Durch die Verwendung der Kontexte und des Kontext-Stacks auf diese Weise knnen viele der Grundlagen der Programmablufe verwaltet werden, einschlielich lokaler und globaler Variablen, Funktionsaufrufen und -rckgaben und so weiter.

Eine besondere Anmerkung zu rekursiven Funktionendas sind Funktionen, die sich selbst aufrufen, mglicherweise ber mehrere Stufen der Rekursion: Jeder rekursive Aufruf der Funktion erstellt einen neuen Ausfhrungskontext. Dies ermglicht es der JavaScript-Laufzeit, die Ebenen der Rekursion und die Rckgabe von Ergebnissen durch diese Rekursion zu verfolgen, bedeutet aber auch, dass jedes Mal, wenn eine Funktion rekursiv aufgerufen wird, mehr Speicher bentigt wird, um den neuen Kontext zu erstellen.

Lauf, JavaScript, lauf

Um JavaScript-Code auszufhren, hlt die Laufzeitumgebung ein Set von Agenten bereit, in dem JavaScript-Code ausgefhrt wird. Jeder Agent besteht aus einem Set von Ausfhrungskontexten, dem Ausfhrungskontext-Stack, einem Hauptthread, einem Set fr alle zustzlichen Threads, die mglicherweise erstellt werden, um Worker zu behandeln, einer Aufgabenwarteschlange und einer Microtask-Warteschlange. Abgesehen vom Hauptthreadden einige Browser ber mehrere Agenten hinweg teilenist jede Komponente eines Agenten einzigartig fr diesen Agenten.

Hier schauen wir uns an, wie die Laufzeitumgebung etwas detaillierter funktioniert.

Event-Loops

Jeder Agent wird von einer Ereignisschleife angetrieben, die wiederholt durchlaufen wird. Whrend jeder Iteration luft hchstens eine ausstehende JavaScript-Aufgabe, dann alle ausstehenden Microtasks, dann wird jegliches notwendige Rendering und Malen durchgefhrt, bevor die Schleife erneut beginnt.

Der Code Ihrer Website oder Anwendung luft im selben Thread, der dieselbe Ereignisschleife verwendet wie die Benutzeroberflche des Webbrowsers selbst. Dies ist der Haupt-Thread, und zustzlich zur Ausfhrung des Hauptcodes Ihrer Website behandelt er das Empfangen und Verteilen von Benutzer- und anderen Ereignissen, das Rendern und Malen von Webinhalten und so weiter.

Die Ereignisschleife treibt also alles an, was im Browser in Bezug auf die Interaktion mit dem Benutzer passiert, ist aber wichtiger fr unsere Zwecke hier auch verantwortlich fr die Planung und Ausfhrung jedes Code-Snippets, das innerhalb ihres Threads luft.

Es gibt drei Arten von Ereignisschleifen:

Fenster-Ereignisschleife

Die Fenster-Ereignisschleife ist diejenige, die alle Fenster antreibt, die einen hnlichen Origin teilen (obwohl es hier, wie unten beschrieben, weitere Beschrnkungen gibt).

Worker-Ereignisschleife

Eine Worker-Ereignisschleife ist eine, die einen Worker antreibt; dies umfasst alle Arten von Workern, einschlielich grundlegender Web-Worker, Shared Worker und Service Worker. Worker befinden sich in einem oder mehreren Agenten, die vom "Haupt"-Code getrennt sind; der Browser kann eine einzelne Ereignisschleife fr alle Worker eines bestimmten Typs verwenden oder mehrere Ereignisschleifen, um sie zu handhaben.

Worklet-Ereignisschleife

Eine Worklet-Ereignisschleife ist die Ereignisschleife, die Agenten antreibt, die den Code fr die Worklets eines bestimmten Agenten ausfhren. Dazu gehren Worklets des Typs Worklet und AudioWorklet.

Mehrere Fenster, die vom gleichen Origin geladen werden, knnen in derselben Ereignisschleife laufen und Aufgaben in die Ereignisschleife einreihen, damit ihre Aufgaben nacheinander bearbeitet werden. Beachten Sie, dass im Web-Jargon das Wort "Fenster" tatschlich "Browser-Level-Container, in dem Webinhalte ausgefhrt werden" bedeutet, einschlielich eines tatschlichen Fensters, eines Tabs oder eines Frames.

Es gibt spezifische Umstnde, in denen dieses Teilen einer Ereignisschleife unter Fenstern mit einem gemeinsamen Origin mglich ist, wie zum Beispiel:

  • Wenn ein Fenster das andere Fenster geffnet hat, teilen diese wahrscheinlich eine Ereignisschleife.
  • Wenn ein Fenster tatschlich ein Container in einem <iframe> ist, teilt es wahrscheinlich eine Ereignisschleife mit dem Fenster, das es enthlt.
  • Die Fenster teilen zufllig denselben Prozess in einer Mehrprozess-Webbrowser-Implementierung.

Die Einzelheiten knnen je nach Browser variieren, je nachdem, wie sie implementiert sind.

Aufgaben vs. Microtasks

Eine Aufgabe ist alles, was durch die Standardmechanismen wie das initiale Starten eines Scripts, das asynchrone Senden eines Ereignisses und so weiter geplant wird. Sie knnen eine Aufgabe auch durch die Verwendung von setTimeout() oder setInterval() in die Warteschlange einreihen.

Der Unterschied zwischen der Aufgabenwarteschlange und der Microtask-Warteschlange ist einfach, aber sehr wichtig:

  • Wenn eine neue Iteration der Ereignisschleife beginnt, fhrt die Laufzeit die nchste Aufgabe aus der Aufgabenwarteschlange aus. Weitere Aufgaben und Aufgaben, die nach Beginn der Iteration zur Warteschlange hinzugefgt werden, werden erst in der nchsten Iteration ausgefhrt.
  • Jedes Mal, wenn eine Aufgabe beendet ist und der Ausfhrungskontext-Stack leer ist, werden alle Microtasks in der Microtask-Warteschlange der Reihe nach ausgefhrt. Der Unterschied besteht darin, dass die Ausfhrung von Microtasks fortgesetzt wird, bis die Warteschlange leer ist, selbst wenn zwischenzeitlich neue Microtasks eingeplant werden. Mit anderen Worten, Microtasks knnen neue Microtasks einreihen und diese neuen Microtasks werden ausgefhrt, bevor die nchste Aufgabe beginnt und bevor die aktuelle Iteration der Ereignisschleife endet.

Probleme

Da Ihr Code im selben Thread luft und die gleiche Ereignisschleife verwendet wie die Benutzeroberflche des Browsers, wird der Browser selbst blockiert, wenn Ihr Code blockiert oder in eine Endlosschleife gert. Selbst eine langsame Leistung, sei es durch einen Fehler oder durch komplexe Arbeiten, die von Ihrem Code ausgefhrt werden, kann dazu fhren, dass der Benutzer einen trgen Browser erlebt.

Wenn mehrere Programme und mehrere Codeobjekte innerhalb dieser Programme gleichzeitig arbeiten mchten, neben einem Browser, der ebenfalls Prozessorzeit bentigt, geschweige denn Zeit, um die Website und ihre eigene Benutzeroberflche zu rendern und Ereignisse zu verarbeiten, wird heute alles viel zu leicht berlastet.

Lsungen

Die Verwendung von Web-Workern, die das Hauptskript erlauben, andere Skripte in neuen Threads auszufhren, hilft, dieses Problem zu lindern. Eine gut gestaltete Website oder App verwendet Worker, um komplexe oder langwierige Operationen durchzufhren, whrend der Haupt-Thread so wenig wie mglich arbeiten muss, abgesehen davon, die Webseite zu aktualisieren, zu layouten und zu rendern.

Dies wird weiter durch die Nutzung von asynchronem JavaScript reduziert, wie z.B. durch Promises, um es dem Hauptcode zu ermglichen, weiterzulaufen, whrend auf das Ergebnis einer Anfrage gewartet wird. Code, der jedoch auf einer grundlegenderen Ebene luftwie Code, der eine Bibliothek oder ein Framework bildetmglicherweise eine Mglichkeit bentigt, um Code zu einem sicheren Zeitpunkt im Haupt-Thread auszufhren, unabhngig von den Ergebnissen einer einzelnen Anfrage oder Aufgabe.

Microtasks sind eine weitere Lsung fr dieses Problem, da sie einen feineren Zugang bieten, indem sie es ermglichen, Code vor Beginn der nchsten Iteration der Ereignisschleife auszufhren, anstatt bis zur nchsten warten zu mssen.

Die Microtask-Warteschlange gibt es schon eine Weile, wurde aber historisch nur intern genutzt, um Dinge wie Promises anzutreiben. Die Hinzufgung von queueMicrotask(), das Entwicklern zur Verfgung gestellt wird, schafft eine einheitliche Warteschlange fr Microtasks, die berall verwendet wird, wo die Notwendigkeit besteht, Code sicher zu einem Zeitpunkt auszufhren, an dem keine Ausfhrungskontexte mehr auf dem JavaScript-Ausfhrungskontext-Stack vorhanden sind. Durch die Verwendung eines standardisierten Warteschlangensystems ber mehrere Instanzen und alle Browser und JavaScript-Laufzeiten hinweg arbeiten diese Microtasks zuverlssig in derselben Reihenfolge und vermeiden so potenziell schwer zu findende Fehler.

Siehe auch


Web Proxy Viewer  |  New URL  |  Original Page