Start · Sprachen · JavaScript · Referenz · ReadableStreamDefaultReader.read()

ReadableStreamDefaultReader.read()

Methode

Liest den nächsten Chunk aus der internen Warteschlange eines <code>ReadableStream</code> und liefert ein Promise mit <code>{ value, done }</code>.

seit JavaScript Baseline: alle modernen Browser (seit 2019), Node. Kategorie: webapi

Signatur

ReadableStreamDefaultReader.prototype.read()

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

Typ
Promise<{ value: any, done: boolean }>
Beschreibung

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'));
Fortschritt: 12 % Fortschritt: 47 % Fortschritt: 100 % Fertig: 204800 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));
Empfangen: Hallo Empfangen: Welt!

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();
{ value: 'a', done: false } { value: 'b', done: false } Abgebrochen: genug gelesen

// Wichtig · Fallstricke

  • Nach done: true ist value immer undefined — den letzten Chunk also nicht im done-Zweig erwarten.
  • Sperre beachten: Solange ein Reader aktiv ist, ist stream.locked true; ein weiterer getReader()-Aufruf, pipeTo() oder response.text() wirft dann einen TypeError. Mit releaseLock() freigeben (idealerweise im finally).
  • Nach releaseLock() liefert read() ein abgelehntes Promise; ausstehende read()-Promises werden in aktuellen Browsern ebenfalls abgelehnt.
  • Chunk-Größe ist nicht garantiert: Bei fetch() sind Chunks Uint8Arrays beliebiger Länge; Mehrbyte-Zeichen können über Chunk-Grenzen verteilt sein — daher TextDecoderStream bzw. TextDecoder mit { 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).