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

Verwenden von Microtasks in JavaScript mit queueMicrotask() - 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

Verwenden von Microtasks in JavaScript mit queueMicrotask()

Eine Microtask ist eine kurze Funktion, die nach der Funktion oder dem Programm ausgefhrt wird, das sie erstellt hat und nur, wenn der JavaScript-Ausfhrungsstapel leer ist, jedoch bevor die Kontrolle wieder an die vom User Agent verwendete Ereignisschleife zurckgegeben wird, die die Ausfhrungsumgebung des Skripts antreibt.

Diese Ereignisschleife kann entweder die Hauptereignisschleife des Browsers oder die Ereignisschleife eines Web Workers sein. Dies ermglicht es der angegebenen Funktion, ohne das Risiko, die Ausfhrung eines anderen Skripts zu stren, zu laufen, und stellt gleichzeitig sicher, dass die Microtask ausgefhrt wird, bevor der User Agent die Mglichkeit hat, auf Aktionen zu reagieren, die von der Microtask durchgefhrt werden.

JavaScript-Promises und die Mutation Observer API verwenden beide die Microtask-Warteschlange, um ihre Rckrufe auszufhren, aber es gibt auch andere Flle, in denen die Mglichkeit, Arbeit bis zum Abschluss der aktuellen Ereignisschleifenpassage zu verschieben, hilfreich ist. Um die Verwendung von Microtasks durch Drittbibliotheken, Frameworks und Polyfills zu ermglichen, wird die Methode queueMicrotask() auf den Schnittstellen Window und WorkerGlobalScope bereitgestellt.

In diesem Artikel

Tasks vs. Microtasks

Um Microtasks richtig zu diskutieren, ist es zunchst ntzlich zu wissen, was eine JavaScript-Task ist und wie sich Microtasks von Tasks unterscheiden. Dies ist eine schnelle, vereinfachte Erklrung, aber wenn Sie mehr Details wnschen, knnen Sie die Informationen im Artikel Im Detail: Microtasks und die JavaScript-Laufzeitumgebung lesen.

Tasks

Eine Task ist alles, was durch die Standardmechanismen ausgefhrt werden soll, wie z.B. das initiale Starten eines Programms, ein asynchron gesendetes Ereignis oder ein ausgelstes Intervall oder Timeout. All diese werden in die Task-Warteschlange eingereiht.

Beispielsweise werden Tasks zur Task-Warteschlange hinzugefgt, wenn:

  • Ein neues JavaScript-Programm oder -Teilprogramm ausgefhrt wird (z. B. von einer Konsole oder durch Ausfhren des Codes in einem <script>-Element) direkt.
  • Der Benutzer auf ein Element klickt. Dann wird eine Task erstellt und alle Ereignisrckrufe werden ausgefhrt.
  • Ein mit setTimeout() oder setInterval() erstelltes Timeout oder Intervall erreicht wird, was dazu fhrt, dass der entsprechende Rckruf in die Task-Warteschlange eingefgt wird.

Die Ereignisschleife, die Ihren Code steuert, bearbeitet diese Tasks nacheinander in der Reihenfolge, in der sie eingereiht wurden. Die lteste ausfhrbare Task in der Task-Warteschlange wird whrend einer einzigen Iteration der Ereignisschleife ausgefhrt. Danach werden Microtasks ausgefhrt, bis die Microtask-Warteschlange leer ist, und dann kann der Browser das Rendering aktualisieren. Dann geht der Browser zur nchsten Iteration der Ereignisschleife ber.

Microtasks

Auf den ersten Blick scheint der Unterschied zwischen Microtasks und Tasks gering zu sein. Und sie sind hnlich; beide bestehen aus JavaScript-Code, der in eine Warteschlange gestellt wird und zur geeigneten Zeit ausgefhrt wird. Der Unterschied liegt jedoch darin, dass die Ereignisschleife nur die Tasks ausfhrt, die zu Beginn der Iteration in der Warteschlange waren, eine nach der anderen, whrend sie die Microtask-Warteschlange ganz anders handhabt.

Es gibt zwei wesentliche Unterschiede:

  1. Jedes Mal, wenn eine Task endet, prft die Ereignisschleife, ob die Task die Kontrolle an anderen JavaScript-Code bergibt. Wenn nicht, fhrt sie alle Microtasks in der Microtask-Warteschlange aus. Die Microtask-Warteschlange wird also mehrfach pro Iteration der Ereignisschleife bearbeitet, auch nach der Bearbeitung von Ereignissen und anderen Rckrufen.
  2. Wenn eine Microtask durch den Aufruf von queueMicrotask() weitere Microtasks zur Warteschlange hinzufgt, werden diese neu hinzugefgten Microtasks ausgefhrt, bevor die nchste Task ausgefhrt wird. Das liegt daran, dass die Ereignisschleife Microtasks weiter aufruft, bis keine mehr in der Warteschlange sind, selbst wenn stndig weitere hinzugefgt werden.

Warnung: Da Microtasks selbst mehr Microtasks einreihen knnen und die Ereignisschleife die Microtasks weiter bearbeitet, bis die Warteschlange leer ist, besteht ein echtes Risiko, dass die Ereignisschleife endlos Microtasks verarbeitet. Gehen Sie vorsichtig vor, wenn Sie rekursiv Microtasks hinzufgen.

Verwendung von Microtasks

Bevor wir weiter ins Detail gehen, ist es wichtig, nochmals zu betonen, dass die meisten Entwickler Microtasks kaum oder gar nicht verwenden werden. Sie sind ein hochspezialisiertes Feature der modernen browserbasierten JavaScript-Entwicklung, das es Ihnen ermglicht, Code vor anderen Dingen in der langen Liste der Dinge, die auf dem Computer des Benutzers passieren, zu planen. Missbrauch dieser Fhigkeit wird zu Leistungsproblemen fhren.

Einreihen von Microtasks

Daher sollten Sie Microtasks typischerweise nur verwenden, wenn es keine andere Lsung gibt oder wenn Sie Frameworks oder Bibliotheken erstellen, die zur Implementierung der von ihnen bereitgestellten Funktionalitt Microtasks nutzen mssen. Whrend es in der Vergangenheit Tricks gab, die es ermglichten, Microtasks einzureihen (wie das sofortige Auflsen eines Promises), fgt die Methode queueMicrotask() eine standardisierte Mglichkeit hinzu, eine Microtask sicher und ohne Tricks einzufhren.

Durch die Einfhrung von queueMicrotask() knnen die Eigenheiten vermieden werden, die auftreten, wenn man sich mit Promises zur Erstellung von Microtasks einmischt. Zum Beispiel werden bei der Verwendung von Promises zur Erstellung von Microtasks Ausnahmen, die vom Rckruf ausgelst werden, als abgelehnte Promises anstatt als Standardausnahmen gemeldet. Auerdem verursachen das Erstellen und Zerstren von Promises zustzlichen Aufwand, sowohl in Bezug auf Zeit als auch auf Speicher, den eine Funktion, die Microtasks ordnungsgem einreiht, vermeidet.

bergeben Sie die JavaScript-Function, die aufgerufen werden soll, whrend der Kontext Microtasks verarbeitet, an die queueMicrotask()-Methode, die im globalen Kontext bereitgestellt wird, wie es entweder von der Window- oder der Worker-Schnittstelle definiert ist, abhngig vom aktuellen Ausfhrungskontext.

js
queueMicrotask(() => {
  /* code to run in the microtask here */
});

Die Microtask-Funktion selbst nimmt keine Parameter entgegen und gibt keinen Wert zurck.

Wann sollen Microtasks verwendet werden

In diesem Abschnitt betrachten wir Szenarien, in denen Microtasks besonders ntzlich sind. Im Allgemeinen geht es darum, Ergebnisse zu erfassen oder zu prfen oder Bereinigungen durchzufhren, nachdem der Hauptkrper eines JavaScript-Ausfhrungskontextes beendet istaber bevor Ereignis-Handler, Timeouts und Intervalle oder andere Rckrufe verarbeitet werden.

Wann ist das ntzlich?

Der Hauptgrund fr die Verwendung von Microtasks ist es, die konsistente Reihenfolge von Tasks sicherzustellen, selbst wenn Ergebnisse oder Daten synchron verfgbar sind, und gleichzeitig das Risiko von fr den Benutzer wahrnehmbaren Verzgerungen bei Operationen zu reduzieren.

Sicherstellen der Reihenfolge bei bedingter Verwendung von Promises

Eine Situation, in der Microtasks verwendet werden knnen, um sicherzustellen, dass die Reihenfolge der Ausfhrung immer konsistent ist, ist, wenn Promises in einer Klausel einer if...else-Anweisung (oder einer anderen bedingten Anweisung) verwendet werden, aber nicht in der anderen Klausel. Betrachten Sie Code wie diesen:

js
customElement.prototype.getData = function (url) {
  if (this.cache[url]) {
    this.data = this.cache[url];
    this.dispatchEvent(new Event("load"));
  } else {
    fetch(url)
      .then((result) => result.arrayBuffer())
      .then((data) => {
        this.cache[url] = data;
        this.data = data;
        this.dispatchEvent(new Event("load"));
      });
  }
};

Das hier eingefhrte Problem besteht darin, dass wir durch die Verwendung einer Task in einem Zweig der if...else-Anweisung (im Fall, in dem das Bild im Cache verfgbar ist), aber indem Promises in der else-Klausel involviert sind, eine Situation haben, in der die Reihenfolge der Operationen variieren kann; zum Beispiel, wie unten gezeigt.

js
element.addEventListener("load", () => console.log("Loaded data"));
console.log("Fetching data");
element.getData();
console.log("Data fetched");

Das zweimalige Ausfhren dieses Codes ergibt die folgenden Ergebnisse.

Wenn die Daten nicht im Cache sind:

Fetching data
Data fetched
Loaded data

Wenn die Daten im Cache sind:

Fetching data
Loaded data
Data fetched

Noch schlimmer ist, dass manchmal die data-Eigenschaft des Elements gesetzt wird, aber andere Male wird sie nicht abgeschlossen, bevor dieser Code abgeschlossen ist.

Wir knnen eine konsistente Reihenfolge dieser Operationen sicherstellen, indem wir eine Microtask in der if-Klausel verwenden, um die beiden Klauseln auszugleichen:

js
customElement.prototype.getData = function (url) {
  if (this.cache[url]) {
    queueMicrotask(() => {
      this.data = this.cache[url];
      this.dispatchEvent(new Event("load"));
    });
  } else {
    fetch(url)
      .then((result) => result.arrayBuffer())
      .then((data) => {
        this.cache[url] = data;
        this.data = data;
        this.dispatchEvent(new Event("load"));
      });
  }
};

Dies gleicht die Klauseln aus, indem beide Situationen das Setzen von data und das Auslsen des load-Ereignisses innerhalb einer Microtask behandeln (Verwendung von queueMicrotask() in der if-Klausel und Verwendung der Promises durch fetch() in der else-Klausel).

Stapelverarbeitung von Operationen

Sie knnen auch Microtasks verwenden, um mehrere Anfragen aus verschiedenen Quellen zu einer einzigen Charge zu sammeln, um den mglichen Aufwand, der mit mehreren Anrufen zur Behandlung derselben Art von Arbeit verbunden ist, zu vermeiden.

Der untenstehende Codeausschnitt erstellt eine Funktion, die mehrere Nachrichten in einem Array stapelt, indem eine Microtask verwendet wird, um sie als ein einziges Objekt zu senden, wenn der Kontext beendet wird.

js
const messageQueue = [];

let sendMessage = (message) => {
  messageQueue.push(message);

  if (messageQueue.length === 1) {
    queueMicrotask(() => {
      const json = JSON.stringify(messageQueue);
      messageQueue.length = 0;
      fetch("url-of-receiver", json);
    });
  }
};

Wenn sendMessage() aufgerufen wird, wird die angegebene Nachricht zunchst dem Nachrichtenwarteschlangen-Array hinzugefgt. Dann wird es interessant.

Wenn die gerade hinzugefgte Nachricht die erste ist, reihen wir eine Microtask ein, die eine Charge sendet. Die Microtask wird ausgefhrt, wie immer, wenn der JavaScript-Ausfhrungspfad die oberste Ebene erreicht, kurz bevor Rckrufe ausgefhrt werden. Das bedeutet, dass alle weiteren Aufrufe von sendMessage(), die in der Zwischenzeit erfolgen, ihre Nachrichten in die Nachrichtenwarteschlange schieben werden, aber aufgrund der Array-Lngenberprfung, bevor eine Microtask hinzugefgt wird, wird keine neue Microtask eingereiht.

Wenn die Microtask ausgefhrt wird, hat sie also ein Array von mglicherweise vielen wartenden Nachrichten. Es beginnt damit, es als JSON mit der JSON.stringify()-Methode zu kodieren. Danach sind die Inhalte des Arrays nicht mehr erforderlich, also leeren wir das messageQueue-Array. Schlielich verwenden wir die fetch()-Methode, um den JSON-String an den Server zu senden.

Dies ermglicht es jedem Aufruf von sendMessage(), der whrend derselben Iteration der Ereignisschleife gemacht wird, ihre Nachrichten an dieselbe fetch()-Operation hinzuzufgen, ohne dass mglicherweise andere Tasks wie Timeouts oder hnliches die bertragung verzgern.

Der Server wird den JSON-String empfangen, dann voraussichtlich dekodieren und die Nachrichten verarbeiten, die er im resultierenden Array findet.

Beispiele

Einfaches Microtask-Beispiel

In diesem einfachen Beispiel sehen wir, dass das Einreihen einer Microtask dazu fhrt, dass der Rckruf der Microtask ausgefhrt wird, nachdem der Hauptkrper dieses obersten Skripts fertig ist.

JavaScript

Im folgenden Code sehen wir einen Aufruf von queueMicrotask(), um eine Microtask auszufhren. Dieser Aufruf wird von Anrufen an log(), einer benutzerdefinierten Funktion, die Text auf dem Bildschirm ausgibt, umrahmt.

js
log("Before enqueueing the microtask");
queueMicrotask(() => {
  log("The microtask has run.");
});
log("After enqueueing the microtask");

Ergebnis

Timeout- und Microtask-Beispiel

In diesem Beispiel wird ein Timeout geplant, das nach null Millisekunden (oder so bald wie mglich) ausgelst wird. Dies demonstriert den Unterschied zwischen dem, was so bald wie mglich bedeutet, wenn eine neue Task geplant wird (wie mit setTimeout()) im Vergleich zur Verwendung einer Microtask.

JavaScript

Im folgenden Code sehen wir einen Aufruf von queueMicrotask(), um eine Microtask auszufhren. Dieser Aufruf wird von Anrufen an log(), einer benutzerdefinierten Funktion, die Text auf dem Bildschirm ausgibt, umrahmt.

Der Code unten plant ein Timeout, das in null Millisekunden auftritt, und reiht dann eine Microtask ein. Dies wird von Aufrufen an log() umrahmt, um zustzliche Nachrichten auszugeben.

js
const callback = () => log("Regular timeout callback has run");

const urgentCallback = () => log("*** Oh noes! An urgent callback has run!");

log("Main program started");
setTimeout(callback, 0);
queueMicrotask(urgentCallback);
log("Main program exiting");

Ergebnis

Beachten Sie, dass die von dem Hauptprogrammkrper protokollierte Ausgabe zuerst erscheint, gefolgt von der Ausgabe der Microtask, gefolgt vom Rckruf des Timeouts. Das liegt daran, dass, wenn die Task, die die Ausfhrung des Hauptprogramms bearbeitet, endet, die Microtask-Warteschlange verarbeitet wird, bevor die Task-Warteschlange, auf der sich der Timeout-Rckruf befindet, bearbeitet wird. Um dies im Kopf zu behalten, denken Sie daran, dass Tasks und Microtasks in separaten Warteschlangen gehalten werden und dass Microtasks zuerst ausgefhrt werden.

Microtask aus einer Funktion

Dieses Beispiel erweitert das vorherige leicht, indem eine Funktion hinzugefgt wird, die eine Arbeit ausfhrt. Diese Funktion verwendet queueMicrotask(), um eine Microtask zu planen. Das Wichtigste, das Sie hier mitnehmen sollten, ist, dass die Microtask nicht verarbeitet wird, wenn die Funktion endet, sondern wenn das Hauptprogramm endet.

JavaScript

Der folgende Hauptprogrammcode folgt. Die doWork()-Funktion hier ruft queueMicrotask() auf, und dennoch wird die Microtasks nicht ausgefhrt, bis das gesamte Programm endet, da das die Task beendet und nichts anderes mehr im Ausfhrungsstapel ist.

js
const callback = () => log("Regular timeout callback has run");

const urgentCallback = () => log("*** Oh noes! An urgent callback has run!");

const doWork = () => {
  let result = 1;

  queueMicrotask(urgentCallback);

  for (let i = 2; i <= 10; i++) {
    result *= i;
  }
  return result;
};

log("Main program started");
setTimeout(callback, 0);
log(`10! equals ${doWork()}`);
log("Main program exiting");

Ergebnis

Siehe auch


Web Proxy Viewer  |  New URL  |  Original Page