Get to know MDN better
Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.
Hinweis: Diese Funktion ist in Web Workers verfügbar.
Die Priorisierte Task-Scheduling-API bietet eine standardisierte Möglichkeit, alle Aufgaben einer Anwendung zu priorisieren, unabhängig davon, ob sie im Code eines Website-Entwicklers oder in Bibliotheken und Frameworks von Drittanbietern definiert sind.
Die Task-Prioritäten sind sehr grob gegliedert und basieren darauf, ob Tasks die Benutzerinteraktion blockieren oder anderweitig die Benutzererfahrung beeinflussen oder im Hintergrund ausgeführt werden können. Entwickler und Frameworks können innerhalb der vom API definierten breiten Kategorien feinere Priorisierungsschemata implementieren.
Die API basiert auf Promises und unterstützt die Möglichkeit, Task-Prioritäten festzulegen und zu ändern, Tasks zum Scheduler hinzuzufügen zu verzögern, Tasks abzubrechen und Änderungen der Priorität sowie Abbruchereignisse zu überwachen.
Die Priorisierte Task-Scheduling-API ist sowohl im Window- als auch im Worker-Thread über die scheduler-Eigenschaft auf dem globalen Objekt verfügbar.
Die Haupt-API-Methoden sind scheduler.postTask() und scheduler.yield(). scheduler.postTask() nimmt eine Rückruffunktion (die Task) an und gibt ein Promise zurück, das mit dem Rückgabewert der Funktion aufgelöst wird oder mit einem Fehler abgelehnt wird. scheduler.yield() verwandelt jede async-Funktion in eine Task, indem der Haupt-Thread für andere Arbeiten an den Browser abgegeben wird, wobei die Ausführung fortgesetzt wird, wenn das zurückgegebene Promise aufgelöst wird.
Die beiden Methoden haben ähnliche Funktionalitäten, bieten jedoch unterschiedliche Kontrollstufen. scheduler.postTask() ist konfigurierbarer — beispielsweise ermöglicht es das explizite Festlegen von Task-Prioritäten sowie das Abbrechen von Tasks über ein AbortSignal. scheduler.yield() hingegen ist einfacher und kann in einer async-Funktion awaited werden, ohne dass eine Folgetask in einer anderen Funktion bereitgestellt werden muss.
Um langlaufende JavaScript-Tasks so aufzuteilen, dass sie den Haupt-Thread nicht blockieren, wird ein scheduler.yield()-Aufruf eingefügt, um den Haupt-Thread vorübergehend zurück an den Browser zu geben, wodurch eine Task erstellt wird, um die Ausführung dort fortzusetzen, wo sie aufgehört hat.
scheduler.yield() gibt ein Promise zurück, das erwartet werden kann, um die Ausführung fortzusetzen. Dies ermöglicht es, Arbeiten, die zur gleichen Funktion gehören, dort einzubinden, ohne den Haupt-Thread zu blockieren, wenn die Funktion ausgeführt wird.
scheduler.yield() nimmt keine Argumente an. Die Task, die ihre Fortsetzung auslöst, hat standardmäßig eine user-visible-Priorität; wenn jedoch scheduler.yield() innerhalb eines scheduler.postTask()-Callbacks aufgerufen wird, erbt es die Priorität der umgebenden Task.
Wenn scheduler.postTask() ohne Argumente aufgerufen wird, erstellt es eine Task mit einer standardmäßigen user-visible-Priorität, die weder abgebrochen noch deren Priorität geändert werden kann.
Da die Methode ein Promise zurückgibt, können Sie dessen Auflösung asynchron mit then() abwarten und Fehler abfangen, die von der Task-Rückruffunktion geworfen werden (oder wenn die Task abgebrochen wird) mit catch. Die Rückruffunktion kann jede Art von Funktion sein (unten zeigen wir eine Pfeilfunktion).
Die gleiche Task könnte mit await/async wie unten gezeigt gewartet werden (beachten Sie, dass dies in einem Immediately Invoked Function Expression (IIFE)) ausgeführt wird:
Sie können auch ein Optionsobjekt an die postTask()-Methode übergeben, wenn Sie das Standardverhalten ändern möchten. Die Optionen sind:
Das gleiche Beispiel wie oben mit einer Prioritätsoption würde so aussehen:
Geplante Tasks werden in Prioritätsreihenfolge ausgeführt, gefolgt von der Reihenfolge, in der sie der Scheduler-Warteschlange hinzugefügt wurden.
Es gibt nur drei Prioritäten, die unten aufgeführt sind (geordnet von hoch nach niedrig):
user-blockingTasks, die Benutzer daran hindern, mit der Seite zu interagieren. Dazu gehört das Rendern der Seite bis zu dem Punkt, an dem sie verwendet werden kann, oder das Reagieren auf Benutzereingaben.
user-visibleTasks, die für den Benutzer sichtbar, aber nicht unbedingt blockierend für Benutzeraktionen sind. Dazu könnte das Rendern von nicht wesentlichen Teilen der Seite gehören, wie z.B. nicht wesentliche Bilder oder Animationen.
Dies ist die Standardpriorität für scheduler.postTask() und scheduler.yield().
backgroundTasks, die nicht zeitkritisch sind. Dazu könnte das Verarbeiten von Protokollen oder das Initialisieren von Drittanbieter-Bibliotheken gehören, die nicht für das Rendering erforderlich sind.
Es gibt viele Anwendungsfälle, in denen die Task-Priorität nie geändert werden muss, während sie in anderen Fällen geändert werden muss. Zum Beispiel könnte das Abrufen eines Bildes von einer background-Task zu user-visible wechseln, wenn ein Karussell in den sichtbaren Bereich gescrollt wird.
Task-Prioritäten können als statisch (unveränderlich) oder dynamisch (änderbar) festgelegt werden, abhängig von den an Scheduler.postTask() übergebenen Argumenten.
Task-Priorität ist unveränderlich, wenn ein Wert im options.priority-Argument angegeben wird. Der angegebene Wert wird für die Task-Priorität verwendet und kann nicht geändert werden.
Die Priorität ist nur dann änderbar, wenn ein TaskSignal an das options.signal-Argument übergeben wird und options.priority nicht gesetzt ist. In diesem Fall übernimmt die Task ihre ursprüngliche Priorität vom Signal, und die Priorität kann anschließend durch Aufrufen von TaskController.setPriority() auf dem Controller, der mit dem Signal verknüpft ist, geändert werden.
Wenn die Priorität weder mit options.priority noch durch Übergeben eines TaskSignal an options.signal festgelegt ist, dann ist sie standardmäßig user-visible (und per Definition unveränderlich).
Beachten Sie, dass eine Task, die abgebrochen werden muss, options.signal entweder auf TaskSignal oder auf AbortSignal setzen muss. Für eine Task mit einer unveränderlichen Priorität zeigt AbortSignal jedoch klarer an, dass die Task-Priorität nicht mit dem Signal geändert werden kann.
Lassen Sie uns ein Beispiel durchgehen, um zu demonstrieren, was wir damit meinen. Wenn Sie mehrere Aufgaben haben, die ungefähr die gleiche Priorität haben, ist es sinnvoll, sie in separate Funktionen zu zerlegen, um die Wartung, das Debuggen und viele andere Gründe zu erleichtern.
Zum Beispiel:
Diese Art von Struktur hilft jedoch nicht beim Blockieren des Haupt-Threads. Da alle fünf Tasks innerhalb einer Hauptfunktion ausgeführt werden, führt der Browser sie alle als eine einzige Task aus.
Um dies zu handhaben, wird häufig eine Funktion periodisch ausgeführt, um den Code dem Haupt-Thread zu unterbrechen. Das bedeutet, dass unser Code in mehrere Tasks aufgeteilt wird, zwischen deren Ausführung der Browser die Möglichkeit hat, hochpriorisierte Tasks wie das Aktualisieren der Benutzeroberfläche zu bearbeiten. Ein übliches Muster für diese Funktion verwendet setTimeout(), um die Ausführung in eine separate Task zu verschieben:
Dies kann innerhalb eines Tasks-Runner-Musters wie folgt verwendet werden, um dem Haupt-Thread nach jeder ausgeführten Task eine Unterbrechung zu ermöglichen:
Um dies weiter zu verbessern, können wir Scheduler.yield verwenden, wenn verfügbar, um diesen Code vor anderen weniger kritischen Aufgaben in der Warteschlange weiter auszuführen:
Enthält die Methoden postTask() und yield() zum Hinzufügen priorisierter Aufgaben zur Planung. Eine Instanz dieser Schnittstelle ist auf den globalen Objekten Window oder WorkerGlobalScope (globalThis.scheduler) verfügbar.
TaskControllerUnterstützt sowohl das Abbrechen einer Aufgabe als auch das Ändern ihrer Priorität.
TaskSignalEin Signalobjekt, das es ermöglicht, eine Aufgabe abzubrechen und ihre Priorität bei Bedarf mithilfe eines TaskController-Objekts zu ändern.
TaskPriorityChangeEventDie Schnittstelle für das prioritychange-Ereignis, das gesendet wird, wenn die Priorität für eine Aufgabe geändert wird.
Hinweis: Wenn die Task-Priorität nie geändert werden muss, können Sie stattdessen einen AbortController und das zugehörige AbortSignal anstelle von TaskController und TaskSignal verwenden.
Diese Eigenschaften sind die Einstiegspunkte für die Verwendung der Scheduler.postTask()-Methode in einem Window- oder Worker-Bereich.
Beachten Sie, dass die unten stehenden Beispiele myLog() verwenden, um in ein Textfeld zu schreiben. Der Code für den Protokollbereich und die Methode wird in der Regel ausgeblendet, um nicht von relevanterem Code abzulenken.
Überprüfen Sie, ob das priorisierte Task-Scheduling unterstützt wird, indem Sie nach der scheduler-Eigenschaft im globalen Bereich testen.
Der unten stehende Code druckt "Feature: Supported", wenn die API in diesem Browser unterstützt wird.
Tasks werden mit Scheduler.postTask() gepostet, indem eine Rückruffunktion (Task) im ersten Argument angegeben wird und ein optionales zweites Argument verwendet werden kann, um die Task-Priorität, das Signal und/oder die Verzögerung anzugeben. Die Methode gibt ein Promise zurück, das mit dem Rückgabewert der Rückruffunktion aufgelöst wird oder mit einem Abbruchfehler oder einem in der Funktion geworfenen Fehler abgelehnt wird.
Da es ein Promise zurückgibt, kann Scheduler.postTask() mit anderen Promises verkettet werden. Im Folgenden zeigen wir, wie man auf die Auflösung des Promises mit then wartet. Dies nutzt die Standardpriorität (user-visible).
Die Methode kann auch mit await innerhalb einer async function verwendet werden. Der unten stehende Code zeigt, wie Sie diesen Ansatz verwenden könnten, um auf eine user-blocking-Task zu warten.
In einigen Fällen müssen Sie möglicherweise überhaupt nicht auf die Fertigstellung warten. Aus Einfachheitsgründen protokollieren viele der hier gezeigten Beispiele einfach das Ergebnis, während die Task ausgeführt wird.
Das unten stehende Protokoll zeigt die Ausgabe der drei oben genannten Tasks. Beachten Sie, dass die Reihenfolge, in der sie ausgeführt werden, zuerst von der Priorität und dann von der Deklarationsreihenfolge abhängt.
Task-Prioritäten können über den priority-Parameter im optionalen zweiten Argument festgelegt werden. In dieser Weise gesetzte Prioritäten sind unveränderlich (können nicht geändert werden).
Unten posten wir zwei Gruppen von drei Tasks, wobei jedes Mitglied in umgekehrter Prioritätsreihenfolge ist. Die letzte Task hat die Standardpriorität. Wenn sie ausgeführt werden, protokolliert jede Task einfach ihre erwartete Reihenfolge (wir warten nicht auf das Ergebnis, weil wir es nicht benötigen, um die Ausführungsreihenfolge zu zeigen).
Die folgende Ausgabe zeigt, dass die Tasks in Prioritätsreihenfolge und dann in der Deklarationsreihenfolge ausgeführt werden.
Task-Prioritäten können auch ihren Anfangswert von einem TaskSignal erhalten, das im optionalen zweiten Argument an postTask() übergeben wird. Wenn dies so eingestellt ist, kann die Priorität der Task dann geändert werden mit dem Controller, der mit dem Signal verbunden ist.
Hinweis: Das Festlegen und Ändern von Task-Prioritäten mit einem Signal funktioniert nur, wenn das options.priority-Argument von postTask() nicht gesetzt ist und wenn das options.signal ein TaskSignal (und kein AbortSignal) ist.
Der unten stehende Code zeigt zunächst, wie man ein TaskController erstellt und die anfängliche Priorität seines Signals zu user-blocking im Konstruktor von TaskController() festlegt.
Der Code verwendet dann addEventListener(), um ein Ereignislistener für das Signal des Controllers hinzuzufügen (alternativ könnten wir die TaskSignal.onprioritychange-Eigenschaft verwenden, um einen Ereignishandler hinzuzufügen). Der Ereignishandler verwendet previousPriority auf dem Ereignis, um die ursprüngliche Priorität zu erhalten, und TaskSignal.priority auf dem Ereignisziel, um die neue/aktuelle Priorität zu erhalten.
Die Aufgabe wird dann gepostet, indem das Signal übergeben wird, und dann ändern wir sofort die Priorität zu background, indem wir TaskController.setPriority() auf dem Controller aufrufen.
Die folgende Ausgabe zeigt, dass die Priorität erfolgreich von user-blocking zu background geändert wurde. Beachten Sie, dass in diesem Fall die Priorität geändert wird, bevor die Task ausgeführt wird, aber sie könnte ebenso während der Task-Ausführung geändert worden sein.
Tasks können mit entweder TaskController und AbortController auf genau die gleiche Weise abgebrochen werden. Der einzige Unterschied ist, dass Sie TaskController verwenden müssen, wenn Sie auch die Priorität der Aufgabe festlegen möchten.
Der folgende Code erstellt einen Controller und übergibt dessen Signal an die Aufgabe. Die Aufgabe wird dann sofort abgebrochen. Dies führt dazu, dass das Promise mit einem AbortError abgelehnt wird, das im catch-Block erfasst und protokolliert wird. Beachten Sie, dass wir auch das abort-Ereignis, das auf dem TaskSignal oder AbortSignal ausgelöst wird, abhören und den Abbruch dort protokollieren könnten.
Das folgende Protokoll zeigt die abgebrochene Aufgabe.
Tasks können verzögert werden, indem eine ganze Zahl von Millisekunden im Parameter options.delay von postTask() angegeben wird. Dies fügt die Aufgabe effektiv in die priorisierte Warteschlange bei einem Timeout hinzu, wie es mit setTimeout() erstellt werden könnte. Die delay ist die minimale Zeitspanne, bevor die Aufgabe zum Scheduler hinzugefügt wird; sie kann länger sein.
Der Code unten zeigt zwei Aufgaben (als Pfeilfunktionen) hinzugefügt mit einer Verzögerung.
Aktualisieren Sie die Seite. Beachten Sie, dass die zweite Zeichenkette nach etwa 2 Sekunden im Protokoll erscheint.
| Prioritized Task Scheduling # scheduler |
| Early detection of input events # the-scheduling-interface |
JavaScript aktivieren, um diese Browser-Kompatibilitätstabelle anzuzeigen.
JavaScript aktivieren, um diese Browser-Kompatibilitätstabelle anzuzeigen.
Der Bauplan für ein besseres Internet.
Besuche die gemeinnützige Muttergesellschaft der Mozilla Corporation, die Mozilla Foundation.
Teile dieses Inhalts sind ©1998–2026 von einzelnen mozilla.org-Mitwirkenden. Inhalte sind verfügbar unter einer Creative-Commons-Lizenz.