Signatur
Beschreibung
Request.prototype.clone() gibt ein neues Request-Objekt zurück, das dieselben Eigenschaften wie das Original hat: URL, Methode, Header, Modus, Credentials, Cache-Einstellung, Redirect-Verhalten, Referrer, Integrity, signal und – ganz wichtig – den Body. Der Body wird dabei intern per Tee auf zwei Streams aufgeteilt, sodass Original und Kopie jeweils unabhängig voneinander gelesen werden können.
Man braucht die Methode immer dann, wenn der Body einer Anfrage mehr als einmal verwendet werden soll. Der Body eines Request ist ein ReadableStream und kann nur einmal konsumiert werden – durch fetch(), text(), json(), formData() usw. Danach ist bodyUsed gleich true. Typische Szenarien sind Wiederholungsversuche (Retry) nach einem Netzwerkfehler, das Protokollieren des Bodys vor dem Absenden oder Service Worker, die eine Anfrage sowohl weiterleiten als auch für eine Offline-Warteschlange speichern.
Die Kopie muss erstellt werden, bevor der Body gelesen wurde. Ist bodyUsed bereits true, wirft clone() einen TypeError.
Rückgabewert
Request-Objekt, das eine exakte, unabhängige Kopie des ursprünglichen Requests ist (inklusive eines eigenen Body-Streams).Beispiele
Body mehrfach lesen
const request = new Request('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Anna' })
});
// Kopie VOR dem Lesen anlegen
const kopie = request.clone();
const text = await request.text();
console.log(text);
console.log(request.bodyUsed, kopie.bodyUsed);
// Die Kopie ist weiterhin lesbar
const daten = await kopie.json();
console.log(daten.name);
Retry-Logik mit fetch()
const fetchMitRetry = async (request, versuche = 3) => {
for (let i = 1; i <= versuche; i++) {
try {
// Jeder Versuch bekommt eine frische Kopie, da fetch() den Body verbraucht
const response = await fetch(request.clone());
if (response.ok) return response;
console.warn(`Versuch ${i}: Status ${response.status}`);
} catch (err) {
console.warn(`Versuch ${i} fehlgeschlagen:`, err.message);
}
await new Promise((r) => setTimeout(r, 500 * i));
}
throw new Error('Alle Versuche fehlgeschlagen');
};
const req = new Request('/api/bestellung', {
method: 'POST',
body: JSON.stringify({ artikel: 42, menge: 1 })
});
const res = await fetchMitRetry(req);
console.log(res.status);
Service Worker: Anfrage weiterleiten und für Offline-Queue sichern
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'POST') return;
// Kopie für den Fehlerfall sichern, bevor fetch() den Body liest
const sicherung = event.request.clone();
event.respondWith(
fetch(event.request).catch(async () => {
const body = await sicherung.text();
// z. B. in IndexedDB ablegen und später erneut senden
console.log('Offline gespeichert:', sicherung.url, body);
return new Response(JSON.stringify({ queued: true }), {
status: 202,
headers: { 'Content-Type': 'application/json' }
});
})
);
});
Fehler: clone() nach dem Lesen
const req = new Request('/api', { method: 'POST', body: 'Hallo' });
await req.text();
try {
req.clone();
} catch (err) {
console.log(err.name);
}
// Wichtig · Fallstricke
- Zeitpunkt entscheidend:
clone()wirft einenTypeError, wenn der Body bereits gelesen wurde (bodyUsed === true). Immer vorfetch()odertext()/json()klonen. - Speicher und Gegendruck: Durch das interne Tee des Streams werden Daten gepuffert, bis beide Zweige gelesen sind. Wird eine Kopie nie konsumiert, bleibt der gesamte Body im Speicher – bei großen Uploads problematisch. Nicht benötigte Kopien vermeiden oder deren Body mit
body.cancel()verwerfen. - Zum Ändern nicht geeignet:
clone()übernimmt alle Eigenschaften unverändert. Um z. B. Header oder URL anzupassen, stattdessennew Request(original, { headers: … })verwenden. - Das
signalwird übernommen: Ein Abbruch über den ursprünglichenAbortControllerbetrifft auch die Kopie. - Requests ohne Body (z. B.
GET) lassen sich jederzeit klonen. - Kompatibilität: In allen modernen Browsern, in Web Workern und Service Workern verfügbar; in Node.js ab Version 18 über das globale
fetch/Request(undici).