Signatur
Beschreibung
read() ist die zentrale Methode, um Daten schrittweise aus einem ReadableStream zu lesen. Jeder Aufruf gibt ein Promise zurück, das mit einem Objekt der Form { value, done } erfüllt wird: Solange Daten vorhanden sind, enthält value den nächsten Chunk und done ist false. Ist der Stream geschlossen, wird { value: undefined, done: true } geliefert.
Typische Einsatzfälle sind das inkrementelle Verarbeiten von fetch()-Antworten über response.body (z. B. Fortschrittsanzeigen, Streaming von Server-Antworten oder LLM-Ausgaben), das Lesen von Dateien über Blob.prototype.stream() oder das Konsumieren selbst erzeugter Streams. Den Reader erhält man über stream.getReader(); dabei wird der Stream gesperrt, sodass kein anderer Konsument gleichzeitig lesen kann.
Ist noch kein Chunk verfügbar, bleibt das Promise ausstehend, bis die Quelle Daten einreiht. Wird der Stream mit einem Fehler beendet, wird das Promise mit diesem Fehler abgelehnt. Nach dem Lesen sollte man den Reader mit releaseLock() freigeben oder den Stream mit reader.cancel() abbrechen, wenn man vorzeitig aufhört.
Rückgabewert
Ein Promise, das erfüllt wird mit:
{ value: chunk, done: false }, wenn ein Chunk verfügbar ist,{ value: undefined, done: true }, wenn der Stream geschlossen ist.
Das Promise wird abgelehnt, wenn der Stream fehlerhaft ist oder der Reader nicht (mehr) an einen Stream gebunden ist (TypeError).
Beispiele
Fetch-Antwort mit Fortschrittsanzeige lesen
const downloadMitFortschritt = async (url) => {
const response = await fetch(url);
const gesamt = Number(response.headers.get('Content-Length')) || 0;
const reader = response.body.getReader();
const chunks = [];
let empfangen = 0;
while (true) {
const { value, done } = await reader.read();
if (done) break;
chunks.push(value);
empfangen += value.byteLength;
if (gesamt) {
console.log(`Fortschritt: ${Math.round((empfangen / gesamt) * 100)} %`);
}
}
return new Blob(chunks);
};
downloadMitFortschritt('/daten/bericht.pdf')
.then((blob) => console.log('Fertig:', blob.size, 'Bytes'));
Text-Streaming zeilenweise dekodieren
const streameText = async (url, onText) => {
const response = await fetch(url);
const reader = response.body
.pipeThrough(new TextDecoderStream())
.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
onText(value); // value ist hier bereits ein String
}
} finally {
reader.releaseLock();
}
};
streameText('/api/stream', (teil) => console.log('Empfangen:', teil));
Eigenen Stream lesen und vorzeitig abbrechen
const stream = new ReadableStream({
start(controller) {
['a', 'b', 'c', 'd'].forEach((z) => controller.enqueue(z));
controller.close();
},
cancel(grund) {
console.log('Abgebrochen:', grund);
}
});
const reader = stream.getReader();
const ersteZwei = async () => {
const erster = await reader.read();
const zweiter = await reader.read();
console.log(erster, zweiter);
await reader.cancel('genug gelesen');
};
ersteZwei();
// Wichtig · Fallstricke
- Nach
done: trueistvalueimmerundefined— den letzten Chunk also nicht imdone-Zweig erwarten. - Sperre beachten: Solange ein Reader aktiv ist, ist
stream.lockedtrue; ein weiterergetReader()-Aufruf,pipeTo()oderresponse.text()wirft dann einenTypeError. MitreleaseLock()freigeben (idealerweise imfinally). - Nach
releaseLock()liefertread()ein abgelehntes Promise; ausstehenderead()-Promises werden in aktuellen Browsern ebenfalls abgelehnt. - Chunk-Größe ist nicht garantiert: Bei
fetch()sind ChunksUint8Arrays beliebiger Länge; Mehrbyte-Zeichen können über Chunk-Grenzen verteilt sein — daherTextDecoderStreambzw.TextDecodermit{ stream: true }verwenden. - Wer vorzeitig aufhört, sollte
reader.cancel()aufrufen, damit die Quelle (z. B. die Netzwerkverbindung) freigegeben wird. - Alternativ lässt sich ein Stream mit
for await (const chunk of stream)lesen (async iteration) — in Firefox, Chrome 124+, Node.js und Safari 26 verfügbar, aber noch nicht überall;read()ist die universell unterstützte Variante. - Für Byte-Streams mit eigenen Puffern (Zero-Copy) gibt es
ReadableStreamBYOBReader.read(view).