Signatur
Beschreibung
Die Schnittstelle Scheduling gehört zur experimentellen Prioritized Task Scheduling- bzw. isInputPending-Erweiterung. Man greift auf eine Instanz nicht per Konstruktor zu, sondern über navigator.scheduling. Ihre einzige Methode ist derzeit isInputPending().
navigator.scheduling.isInputPending() gibt true zurück, wenn in der Ereigniswarteschlange Eingaben wie Klicks, Tastendrücke oder Touch-Ereignisse auf Verarbeitung warten. So kann ein langer, in Teilstücke aufgeteilter Rechenvorgang nur dann an den Browser abgeben, wenn wirklich etwas ansteht. Man muss also nicht nach einem festen Zeitplan, zum Beispiel alle 50 ms, unterbrechen. Das hält die Seite reaktionsfähig und verbessert Kennzahlen wie Interaction to Next Paint (INP), ohne unnötig Durchsatz zu verlieren.
Typische Einsatzfälle sind das Verarbeiten großer Datenmengen im Hauptthread, das schrittweise Aufbauen umfangreicher DOM-Strukturen und Hydration-Schritte in Frameworks. Die Methode bricht selbst nichts ab. Sie liefert nur einen Hinweis. Das eigentliche Abgeben an den Browser muss man selbst erledigen, etwa mit setTimeout(), MessageChannel oder scheduler.yield().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | object | {} | Optionales Einstellungsobjekt für isInputPending(options). |
| $options.includeContinuous | boolean | false | Bei true zählen auch fortlaufende Ereignisse wie mousemove, pointermove oder wheel als wartende Eingabe. Bei false werden nur einzelne Ereignisse wie click oder keydown berücksichtigt. |
Rückgabewert
isInputPending() gibt true zurück, wenn Benutzereingaben auf Verarbeitung warten, sonst false. Ist die API nicht verfügbar, ist navigator.scheduling undefined.Beispiele
Verfügbarkeit prüfen
const hasInputPending =
'scheduling' in navigator &&
typeof navigator.scheduling.isInputPending === 'function';
console.log(hasInputPending ? 'isInputPending wird unterstützt' : 'Nicht unterstützt');
Lange Aufgabe nur bei wartender Eingabe unterbrechen
const yieldToMain = () => new Promise((resolve) => setTimeout(resolve, 0));
const processItems = async (items, handle) => {
const DEADLINE_MS = 50;
let start = performance.now();
for (const item of items) {
handle(item);
const inputWaiting = navigator.scheduling?.isInputPending?.() ?? false;
const overBudget = performance.now() - start > DEADLINE_MS;
if (inputWaiting || overBudget) {
await yieldToMain(); // Browser verarbeitet Eingaben/Rendering
start = performance.now();
}
}
};
const data = Array.from({ length: 100_000 }, (_, i) => i);
let sum = 0;
processItems(data, (n) => { sum += Math.sqrt(n); })
.then(() => console.log('Fertig, Summe:', Math.round(sum)));
Fortlaufende Ereignisse (Mausbewegung) einbeziehen
const renderChunk = (list, rows, from, to) => {
for (let i = from; i < to && i < rows.length; i++) {
const li = document.createElement('li');
li.textContent = rows[i];
list.append(li);
}
};
const renderAll = async (rows) => {
const list = document.querySelector('#liste');
let i = 0;
while (i < rows.length) {
renderChunk(list, rows, i, i + 200);
i += 200;
// Auch bei Scroll- oder Mausbewegungen abgeben
if (navigator.scheduling?.isInputPending({ includeContinuous: true })) {
await new Promise((r) => setTimeout(r, 0));
}
}
console.log(`${rows.length} Zeilen gerendert`);
};
renderAll(Array.from({ length: 5000 }, (_, n) => `Eintrag ${n + 1}`));
Moderne Variante mit scheduler.yield() und Rückfallebene
const yieldToMain = () =>
globalThis.scheduler?.yield
? scheduler.yield()
: new Promise((resolve) => setTimeout(resolve, 0));
const work = async (tasks) => {
for (const task of tasks) {
task();
if (navigator.scheduling?.isInputPending?.() ?? true) {
await yieldToMain();
}
}
console.log('Alle Aufgaben erledigt');
};
work([() => 1 + 1, () => 'a'.repeat(10), () => [3, 1, 2].sort()]);
// Wichtig · Fallstricke
- Experimentell und nur in Chromium verfügbar: Firefox und Safari unterstützen die API nicht. Prüfe deshalb immer mit
navigator.scheduling?.isInputPending, ob sie vorhanden ist, und sieh eine Ersatzlösung vor, etwa einen reinen Zeitbudget-Ansatz. - Nur ein Hinweis, keine Garantie: Ein Rückgabewert von
falseheißt nicht, dass man nie abgeben muss. Auch Rendering, Animationen und Netzwerk-Callbacks brauchen den Hauptthread. Kombiniere die Abfrage daher mit einer Zeitgrenze von etwa 50 ms. - Eingaben in Cross-Origin-iframes werden aus Datenschutzgründen nicht immer erkannt. Das Ergebnis kann also
falsesein, obwohl der Nutzer gerade in einem eingebetteten Frame interagiert. - Das Chrome-Team empfiehlt inzwischen meist
scheduler.yield()oderscheduler.postTask(), weil regelmäßiges Abgeben auch ohneisInputPending()gut funktioniert und breiter unterstützt wird. - Die Methode ist nur im Hauptthread des Fensters verfügbar, nicht in Web Workern.
- Ein Aufruf nach jedem einzelnen, sehr kleinen Arbeitsschritt verursacht messbaren Mehraufwand. Frage besser nach einem Block von Einträgen ab.