Signatur
Beschreibung
TaskController gehört zur Prioritized Task Scheduling API und erweitert AbortController. Neben dem bekannten abort() kann er mit setPriority() die Priorität aller Aufgaben ändern, die mit seinem Signal verknüpft sind. Seine Eigenschaft signal ist ein TaskSignal (Unterklasse von AbortSignal). Dieses Signal hat zusätzlich die Eigenschaft priority und löst das Ereignis prioritychange aus.
Verwendet wird der Controller zusammen mit scheduler.postTask(callback, { signal }). Alle Aufgaben, die dasselbe Signal bekommen, bilden eine Gruppe: Ein einziger Aufruf von setPriority() stuft die ganze Gruppe hoch oder herunter, und ein abort() bricht alle Aufgaben ab, die noch nicht gestartet sind. Das Promise dieser abgebrochenen Aufgaben wird mit dem Abbruchgrund abgelehnt, standardmäßig einem DOMException vom Typ AbortError.
Typische Einsätze sind das Vorladen oder Vorberechnen von Daten mit niedriger Priorität ("background"). Braucht der Nutzer das Ergebnis plötzlich doch, stuft man die Gruppe auf "user-blocking" hoch. Wird es nicht mehr gebraucht, zum Beispiel beim Wechsel der Route in einer Single-Page-Anwendung, bricht man die Gruppe ab.
Es gibt drei Prioritäten: "user-blocking" (höchste), "user-visible" (Standard) und "background" (niedrigste).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | { priority?: "user-blocking" | "user-visible" | "background" } | { priority: "user-visible" } | Optionales Objekt für den Konstruktor new TaskController(options). priority legt die Anfangspriorität des zugehörigen TaskSignal fest. |
| $priority (setPriority) Pflicht | "user-blocking" | "user-visible" | "background" | Argument von controller.setPriority(priority): die neue Priorität für alle Aufgaben, die mit controller.signal verknüpft sind. Löst am Signal das Ereignis prioritychange aus. |
|
| $reason (abort) | any | DOMException "AbortError" | Argument von controller.abort(reason) (von AbortController geerbt): der Abbruchgrund, mit dem die Promises der wartenden Aufgaben abgelehnt werden. |
Rückgabewert
TaskController-Instanz. Ihre Eigenschaft signal ist ein TaskSignal. setPriority() und abort() geben undefined zurück.Beispiele
Controller erzeugen und Aufgabe planen
const controller = new TaskController({ priority: 'background' });
console.log(controller.signal.priority); // Anfangspriorität
const result = await scheduler.postTask(
() => {
// aufwendige Berechnung im Hintergrund
return Array.from({ length: 1e5 }, (_, i) => i).reduce((a, b) => a + b, 0);
},
{ signal: controller.signal }
);
console.log('Summe:', result);
Priorität einer Aufgabengruppe nachträglich erhöhen
const prefetch = new TaskController({ priority: 'background' });
prefetch.signal.addEventListener('prioritychange', (event) => {
console.log(`Priorität: ${event.previousPriority} → ${event.target.priority}`);
});
const loadArticle = (id) =>
scheduler.postTask(
() => fetch(`/api/articles/${id}`).then((r) => r.json()),
{ signal: prefetch.signal }
);
const tasks = [1, 2, 3].map(loadArticle);
// Der Nutzer öffnet die Artikelliste, jetzt wird es dringend
document.querySelector('#articles-tab')?.addEventListener('click', () => {
prefetch.setPriority('user-blocking');
});
const articles = await Promise.all(tasks);
console.log(articles.length, 'Artikel geladen');
Wartende Aufgaben beim Routenwechsel abbrechen
const routeTasks = new TaskController();
const task = scheduler
.postTask(() => console.log('Wird nie ausgeführt'), {
signal: routeTasks.signal,
delay: 1000
})
.catch((err) => console.log('Abgebrochen:', err.name));
// z. B. beim Verlassen der Seite in einer SPA
routeTasks.abort();
await task;
console.log('signal.aborted =', routeTasks.signal.aborted);
Feature-Erkennung mit Rückfallebene
const runLowPriority = (fn) => {
if ('scheduler' in globalThis && 'TaskController' in globalThis) {
const controller = new TaskController({ priority: 'background' });
return { controller, done: scheduler.postTask(fn, { signal: controller.signal }) };
}
// Rückfall: einfache Verzögerung per setTimeout
const controller = new AbortController();
const done = new Promise((resolve, reject) => {
const id = setTimeout(() => resolve(fn()), 0);
controller.signal.addEventListener('abort', () => {
clearTimeout(id);
reject(controller.signal.reason);
});
});
return { controller, done };
};
const { done } = runLowPriority(() => 'fertig');
console.log(await done);
// Wichtig · Fallstricke
- Browser-Kompatibilität: In Chromium-Browsern gibt es die API seit Version 94, in Firefox erst in neueren Versionen. Safari unterstützt sie nicht. Prüfe deshalb immer, ob
'TaskController' in globalThisgilt, und sieh einen Rückfall vor (z. B.setTimeoutoder ein Polyfill). - Die Option
priorityinpostTask()hat Vorrang: Übergibst du sowohlsignalals auchpriority, nutzt die Aufgabe die festepriority. Ein späteressetPriority()wirkt dann nicht auf sie; das Signal ist nur noch für den Abbruch zuständig. setPriority()wirft einenNotAllowedError, wenn du es innerhalb einesprioritychange-Handlers desselben Signals aufrufst. Die Priorität zu setzen, die bereits gilt, bewirkt nichts und löst auch kein Ereignis aus.abort()verhindert nur den Start wartender Aufgaben. Eine Callback-Funktion, die schon läuft, wird nicht unterbrochen. Bei längeren Aufgaben solltest du deshalb im Callback selbstsignal.abortedprüfen odersignal.throwIfAborted()aufrufen.- Fange abgelehnte Promises abgebrochener Aufgaben ab. Sonst erscheinen Warnungen über unbehandelte Ablehnungen (
unhandledrejection). - Einen abgebrochenen Controller kann man nicht wiederverwenden. Für eine neue Aufgabengruppe erzeugst du einen neuen
TaskController.