Signatur
Beschreibung
DeferredRequestInit ist ein Wörterbuch (Dictionary) der Fetch API. Es wird als zweites Argument an fetchLater(resource, options) übergeben. Es enthält alle Optionen aus RequestInit, also method, headers, body, signal, credentials und so weiter. Dazu kommt nur ein neues Feld: activateAfter.
Mit fetchLater() legt man eine Anfrage beim Browser ab, die er später sendet. Das passiert spätestens, wenn das Dokument entladen oder aus dem Back/Forward-Cache entfernt wird. Mit activateAfter kann die Anfrage schon früher gesendet werden, nämlich nach einer Wartezeit in Millisekunden. Damit werden navigator.sendBeacon() und fetch(..., { keepalive: true }) in unload- oder pagehide-Handlern ersetzt. Solche Handler sind unzuverlässig, vor allem auf Mobilgeräten.
Typische Einsätze sind Analysedaten, Sitzungsende-Berichte und das Sammeln von Leistungsmetriken. In all diesen Fällen soll ein Beacon sicher ankommen, auch wenn der Nutzer den Tab schließt. Solange die Anfrage noch nicht gesendet wurde, kann man sie mit einem AbortSignal abbrechen und durch eine neue ersetzen. Das ist ein gängiges Muster, um Daten laufend zu sammeln.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $activateAfter | number (DOMHighResTimeStamp) | null (keine Wartezeit, gesendet wird erst beim Entladen) | Wartezeit in Millisekunden. Danach sendet der Browser die Anfrage, auch wenn das Dokument noch aktiv ist. Ein negativer Wert löst einen RangeError aus. |
| $method | string | "GET" | HTTP-Methode, meist "POST" für Beacons. |
| $headers | Headers | Object | Array | Anfrage-Header, z. B. { "Content-Type": "application/json" }. |
|
| $body | string | Blob | FormData | URLSearchParams | ArrayBuffer | TypedArray | Anfragekörper. Ein ReadableStream ist nicht erlaubt, denn die Größe muss vorher bekannt sein. Der Körper zählt zum Kontingent für verzögerte Anfragen. |
|
| $signal | AbortSignal | Damit lässt sich die verzögerte Anfrage abbrechen, solange sie noch nicht gesendet wurde. | |
| $credentials | "omit" | "same-origin" | "include" | "same-origin" | Legt fest, ob Cookies und Anmeldedaten mitgesendet werden. |
| $referrerPolicy | string | Referrer-Richtlinie der Anfrage. | |
| $priority | "high" | "low" | "auto" | "auto" | Hinweis zur Priorität der Anfrage. |
Rückgabewert
DeferredRequestInit ist ein Optionen-Wörterbuch und hat selbst keinen Rückgabewert. fetchLater() gibt damit ein FetchLaterResult-Objekt zurück. Dessen Eigenschaft activated zeigt an, ob die Anfrage schon gesendet wurde.Beispiele
Einfacher Sitzungs-Beacon beim Verlassen der Seite
// Wird spätestens gesendet, wenn die Seite entladen wird
const options = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event: 'session_end', ts: Date.now() })
};
const result = fetchLater('https://example.com/analytics', options);
console.log(result.activated);
Beacon mit Wartezeit über activateAfter
// Nach spätestens 60 Sekunden senden, auch wenn der Tab offen bleibt
const result = fetchLater('/api/heartbeat', {
method: 'POST',
body: new URLSearchParams({ page: location.pathname }),
activateAfter: 60_000
});
setTimeout(() => {
console.log('Gesendet:', result.activated);
}, 61_000);
Laufend gesammelte Daten: alte Anfrage abbrechen und ersetzen
let controller = null;
const metrics = [];
const scheduleReport = () => {
// Vorherige, noch nicht gesendete Anfrage verwerfen
controller?.abort();
controller = new AbortController();
fetchLater('/api/metrics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(metrics),
signal: controller.signal,
activateAfter: 5 * 60_000 // spätestens nach 5 Minuten
});
};
document.addEventListener('click', (e) => {
metrics.push({ tag: e.target.tagName, t: performance.now() });
scheduleReport();
});
Funktionserkennung mit Rückfall auf sendBeacon
const sendDeferred = (url, data) => {
const body = JSON.stringify(data);
if ('fetchLater' in window) {
try {
fetchLater(url, { method: 'POST', body, activateAfter: 30_000 });
return;
} catch (err) {
// z. B. QuotaExceededError bei zu großem body
console.warn('fetchLater fehlgeschlagen:', err.name);
}
}
// Rückfall für Browser ohne fetchLater()
addEventListener('pagehide', () => navigator.sendBeacon(url, body), { once: true });
};
sendDeferred('/api/log', { level: 'info', msg: 'Seite geladen' });
// Wichtig · Fallstricke
- Browser-Unterstützung: Die Schnittstelle ist experimentell und derzeit nur in Chromium-Browsern verfügbar (ab Chrome 135). Nutzen Sie immer eine Funktionserkennung mit
'fetchLater' in windowund sehen Sie einen Rückfall vor, etwanavigator.sendBeacon(). - Nur sichere Kontexte: Die Ziel-URL muss HTTPS verwenden (oder
localhost). Sonst löstfetchLater()einenTypeErroraus. - Kein Stream-Körper: Ist
bodyeinReadableStream, gibt es einenTypeError. - Kontingent: Verzögerte Anfragen haben ein begrenztes Kontingent pro Dokument und Origin, standardmäßig rund 64 KiB pro Origin. Beim Überschreiten wird ein
QuotaExceededErrorausgelöst. Halten Sie Nutzdaten klein, und brechen Sie überholte Anfragen persignalab, damit sie das Kontingent nicht weiter belegen. keepalivemuss man nicht setzen, denn verzögerte Anfragen verhalten sich automatisch so.- Einen negativen Wert für
activateAfterlehnt der Browser mit einemRangeErrorab. Die Wartezeit ist eine Obergrenze. Der Browser kann die Anfrage auch früher senden, zum Beispiel beim Entladen des Dokuments. - Eine Antwort des Servers bekommt man nicht zu sehen, denn
fetchLater()gibt keinPromise<Response>zurück. Es eignet sich also nur für Anfragen, bei denen man die Antwort nicht braucht. - Iframes aus anderen Origins dürfen
fetchLater()nur nutzen, wenn die Permissions Policydeferred-fetches erlaubt.