Signatur
Beschreibung
scheduler.yield() gibt ein Promise zurück, das aufgelöst wird, nachdem der Browser Gelegenheit hatte, andere Arbeit zu erledigen, etwa Eingaben verarbeiten, rendern oder Ereignisse behandeln. Mit await scheduler.yield() teilst du eine lange Aufgabe in mehrere kurze auf. Dadurch bleibt die Seite reaktionsfähig, und Kennzahlen wie Interaction to Next Paint (INP) verbessern sich.
Der entscheidende Unterschied zu älteren Tricks wie await new Promise(r => setTimeout(r, 0)): Die Fortsetzung wird nicht ans Ende der Aufgabenwarteschlange gestellt. Sie läuft als priorisierte Fortsetzung vor anderen Aufgaben gleicher Priorität. Die Seite bleibt reaktionsfähig, und trotzdem kann dein Code nicht von beliebigen anderen Skripten (z. B. Werbe- oder Tracking-Code) „überholt“ werden.
Ruft man yield() innerhalb einer mit scheduler.postTask() geplanten Aufgabe auf, übernimmt die Fortsetzung deren Priorität ("user-blocking", "user-visible" oder "background") und deren AbortSignal. Außerhalb von postTask(), etwa in einem Ereignis-Handler, gilt die Priorität "user-visible".
Einsatzgebiete sind Schleifen über große Datenmengen, aufwendige Berechnungen im Haupt-Thread, schrittweises Rendern großer Listen und Ereignis-Handler, die erst schnell sichtbares Feedback geben und dann den Rest nachziehen sollen.
Rückgabewert
undefined aufgelöst wird, sobald die Fortsetzung an der Reihe ist. Wurde das übernommene AbortSignal (aus einem umgebenden postTask()) abgebrochen, wird es mit dem reason des Signals abgelehnt, standardmäßig eine DOMException namens "AbortError".Beispiele
Lange Schleife in Häppchen aufteilen
const items = Array.from({ length: 50_000 }, (_, i) => i);
const processItem = (n) => {
// simulierte teure Arbeit
let x = 0;
for (let i = 0; i < 1_000; i++) x += Math.sqrt(n + i);
return x;
};
const processAll = async () => {
let deadline = performance.now() + 50; // max. ca. 50 ms am Stück
let sum = 0;
for (const item of items) {
sum += processItem(item);
if (performance.now() >= deadline) {
await scheduler.yield(); // Browser darf rendern und Eingaben verarbeiten
deadline = performance.now() + 50;
}
}
console.log('Fertig:', items.length, 'Einträge');
};
processAll();
Feature-Erkennung mit Fallback
const yieldToMain = () => {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
// Fallback: Fortsetzung landet am Ende der Warteschlange
return new Promise((resolve) => setTimeout(resolve, 0));
};
const tasks = [
() => console.log('Validierung'),
() => console.log('Speichern'),
() => console.log('Analytics senden'),
];
const run = async () => {
for (const task of tasks) {
task();
await yieldToMain();
}
};
run();
Ereignis-Handler: erst Feedback, dann Rest
const button = document.querySelector('#save');
const status = document.querySelector('#status');
button.addEventListener('click', async () => {
// 1. Sofort sichtbares Feedback
status.textContent = 'Wird gespeichert …';
// 2. Abgeben, damit der Browser das Feedback zeichnen kann
await scheduler.yield();
// 3. Teure Folgearbeit
const data = JSON.stringify({ zeit: Date.now(), inhalt: 'x'.repeat(1e6) });
localStorage.setItem('entwurf', data);
status.textContent = 'Gespeichert';
});
Priorität und Abbruch aus postTask() übernehmen
const controller = new TaskController({ priority: 'background' });
const job = scheduler.postTask(async () => {
for (let i = 0; i < 5; i++) {
console.log('Schritt', i);
// Fortsetzung bleibt 'background' und hängt am selben Signal
await scheduler.yield();
}
}, { signal: controller.signal });
// Nach kurzer Zeit abbrechen
setTimeout(() => controller.abort(), 0);
job.catch((err) => console.log('Abgebrochen:', err.name));
// Wichtig · Fallstricke
- Browser-Unterstützung: Chromium-Browser ab Version 129 und Firefox ab Version 142 unterstützen die Methode, Safari noch nicht. Prüfe deshalb immer mit
'scheduler' in globalThis && 'yield' in schedulerund sieh einen Fallback übersetTimeoutvor. Das Beispiel zum Abbruch benutzt außerdempostTask()undTaskController, die ebenfalls nicht überall verfügbar sind. - Keine Parameter: Frühe Entwürfe und Origin Trials kannten Optionen wie
{ priority, signal }. Die aktuelle Spezifikation übernimmt beides ausschließlich aus dem umgebendenpostTask()-Kontext. - Nicht zu oft abgeben: Jedes
yield()kostet etwas Verwaltungsaufwand. Gib zeitbasiert ab (z. B. alle 50 ms, das entspricht der Schwelle für „Long Tasks“) und nicht nach jedem einzelnen Element. - Zustand kann sich ändern: Zwischen
await scheduler.yield()und der Fortsetzung können Nutzereingaben oder andere Skripte DOM und Daten verändert haben. Prüfe wichtige Annahmen nach dem Abgeben erneut. - Kein Ersatz für Worker: Die Arbeit läuft weiterhin im Haupt-Thread. Für wirklich rechenintensive Aufgaben ist ein
Web Workermeist die bessere Wahl. - Die Methode gibt es im
Windowund in Workern über das globale Objektscheduler. Aufgerufen wird sie immer alsscheduler.yield(). Eine Instanz vonSchedulererzeugt man nicht selbst.