Signatur
Beschreibung
requestIdleCallback() plant eine Funktion so ein, dass der Browser sie erst ausführt, wenn der Hauptthread gerade nichts zu tun hat, meist am Ende eines Frames oder wenn keine Eingaben, kein Layout und keine Animationen anstehen. So lassen sich unwichtige Hintergrundarbeiten wie Analytics-Daten senden, Daten vorladen, Caches aufwärmen oder große Datenmengen häppchenweise verarbeiten, ohne dass Scrollen, Tippen oder Animationen ruckeln.
Die Callback-Funktion bekommt ein IdleDeadline-Objekt übergeben. Dessen Methode timeRemaining() gibt an, wie viele Millisekunden in der aktuellen Leerlaufphase noch übrig sind (höchstens 50 ms). Die Eigenschaft didTimeout ist true, wenn der Callback nur deshalb läuft, weil das angegebene timeout abgelaufen ist. Üblich ist, in einer Schleife so lange Arbeitspakete abzuarbeiten, wie timeRemaining() größer als 0 ist, und für den Rest einen neuen Callback anzufordern.
Es gibt keine Garantie, dass der Callback überhaupt aufgerufen wird. Bei dauerhaft ausgelastetem Hauptthread oder in Hintergrund-Tabs kann er sehr lange warten. Für Arbeit, die spätestens nach einer bestimmten Zeit erledigt sein muss, gibt man die Option timeout an. Mit cancelIdleCallback(id) lässt sich ein geplanter Callback wieder abbrechen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $callback Pflicht | (deadline: IdleDeadline) => void | Funktion, die in einer Leerlaufphase aufgerufen wird. Sie erhält ein IdleDeadline-Objekt mit timeRemaining() und didTimeout. |
|
| $options | { timeout?: number } | {} | Optionales Objekt. timeout (in Millisekunden): Ist der Callback bis dahin noch nicht gelaufen, wird er beim nächsten Durchlauf der Ereignisschleife auf jeden Fall ausgeführt, auch wenn das die Leistung beeinträchtigt. |
Rückgabewert
window.cancelIdleCallback(id) abbrechen lässt.Beispiele
Einfacher Hintergrund-Task mit Timeout
const id = requestIdleCallback((deadline) => {
console.log('Leerlauf erreicht, Restzeit:', Math.round(deadline.timeRemaining()), 'ms');
console.log('Wegen Timeout ausgelöst?', deadline.didTimeout);
}, { timeout: 2000 });
console.log('Geplant mit ID', id);
Große Warteschlange häppchenweise abarbeiten
const queue = Array.from({ length: 10000 }, (_, i) => i);
const results = [];
const processQueue = (deadline) => {
// Arbeiten, solange Zeit übrig ist oder der Timeout gegriffen hat
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && queue.length > 0) {
const item = queue.shift();
results.push(item * item);
}
if (queue.length > 0) {
requestIdleCallback(processQueue, { timeout: 1000 });
} else {
console.log('Fertig:', results.length, 'Einträge verarbeitet');
}
};
requestIdleCallback(processQueue, { timeout: 1000 });
Analytics-Ereignisse gesammelt senden, mit Fallback
// Fallback für Browser ohne requestIdleCallback (z. B. Safari)
const idle = window.requestIdleCallback ??
((cb) => setTimeout(() => cb({ didTimeout: false, timeRemaining: () => 0 }), 1));
const pending = [];
let scheduled = false;
const track = (event) => {
pending.push({ event, ts: Date.now() });
if (!scheduled) {
scheduled = true;
idle(() => {
navigator.sendBeacon('/analytics', JSON.stringify(pending.splice(0)));
scheduled = false;
}, { timeout: 3000 });
}
};
track('page_view');
track('button_click');
Geplanten Callback abbrechen
const id = requestIdleCallback(() => {
console.log('Vorladen gestartet');
});
// Benutzer verlässt die Ansicht, Vorladen nicht mehr nötig
cancelIdleCallback(id);
console.log('Callback', id, 'abgebrochen');
// Wichtig · Fallstricke
- Browser-Kompatibilität: Chrome, Edge und Firefox unterstützen die Methode schon lange. In Safari ist sie standardmäßig nicht verfügbar, daher vorher prüfen (
'requestIdleCallback' in window) und aufsetTimeoutzurückfallen. - Keine DOM-Änderungen im Callback: Der Callback läuft am Ende eines Frames, wenn das Layout schon berechnet ist. DOM-Änderungen lösen dann ein erneutes Layout aus und können das Zeitbudget überschreiten. DOM-Updates besser mit
requestAnimationFrame()einplanen. - Zeitbudget beachten:
timeRemaining()liefert höchstens 50 ms. Lang laufende Einzelaufgaben, auch Promise-Auflösungen mit viel Arbeit, blockieren trotzdem den Hauptthread. - Timeout sparsam einsetzen: Mit
timeoutwird der Callback notfalls auch mitten in Lastphasen erzwungen. Dann istdidTimeouttrueundtimeRemaining()meist 0. Den Code so schreiben, dass er in diesem Fall trotzdem sinnvoll arbeitet. - In Hintergrund-Tabs werden Callbacks unter Umständen stark verzögert (oft nur etwa einmal pro 10 s). Für Arbeit, die garantiert erledigt werden muss (z. B. vor dem Schließen der Seite), ist die Methode nicht geeignet. Dafür lieber
visibilitychangemitnavigator.sendBeacon()nutzen. - Die Methode gibt es auch in Web Workern nicht überall. Neuere Alternative mit Prioritäten:
scheduler.postTask()(Prioritized Task Scheduling API, noch nicht in allen Browsern).