Start · Sprachen · JavaScript · Referenz · ReadableStreamDefaultReader.closed

ReadableStreamDefaultReader.closed

Eigenschaft

Liefert ein <code>Promise</code>, das erfüllt wird, sobald der Stream geschlossen ist. Es wird abgelehnt, wenn der Stream fehlschlägt oder die Sperre des Readers freigegeben wird.

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

Signatur

ReadableStreamDefaultReader.closed

Beschreibung

Die schreibgeschützte Eigenschaft closed eines ReadableStreamDefaultReader gibt ein Promise<undefined> zurück. Mit diesem Promise lässt sich das Ende eines Streams beobachten, ohne die Leseschleife selbst auszuwerten. Das Promise wird erfüllt, sobald der Stream vollständig geschlossen ist, also nach controller.close() und nachdem alle Chunks in der Warteschlange gelesen wurden. Es wird abgelehnt, wenn der Stream in einen Fehlerzustand gerät, zum Beispiel durch controller.error() oder einen Netzwerkabbruch.

Es gibt noch einen zweiten Grund für eine Ablehnung: Gibt man die Sperre mit reader.releaseLock() frei, bevor der Stream geschlossen ist, wird closed mit einem TypeError abgelehnt. Der Reader ist dann nicht mehr mit dem Stream verbunden.

Typische Einsatzfälle:

  • Aufräumarbeiten nach dem Ende eines Downloads, etwa eine Fortschrittsanzeige ausblenden
  • Zentrale Fehlerbehandlung unabhängig von der Leseschleife
  • Feststellen, ob ein Reader noch gültig ist

Die Eigenschaft gibt bei jedem Zugriff dasselbe Promise-Objekt zurück. Man kann also beliebig viele Handler daran hängen.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das mit undefined erfüllt wird, wenn der Stream geschlossen ist. Es wird mit dem Fehlergrund des Streams abgelehnt oder mit einem TypeError, wenn die Sperre des Readers freigegeben wurde.

Beispiele

Auf das Ende eines Streams warten

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue('Hallo');
    controller.enqueue('Welt');
    controller.close();
  }
});

const reader = stream.getReader();

const leseAlles = async () => {
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    console.log('Chunk:', value);
  }
  await reader.closed; // erfüllt, weil der Stream geschlossen ist
  console.log('Stream geschlossen');
};

leseAlles();
Chunk: Hallo Chunk: Welt Stream geschlossen

Stream-Fehler zentral abfangen

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue('Teil 1');
    setTimeout(() => controller.error(new Error('Verbindung abgebrochen')), 100);
  }
});

const reader = stream.getReader();

reader.closed
  .then(() => console.log('Fertig'))
  .catch((err) => console.error('Stream-Fehler:', err.message));

const lesen = async () => {
  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      console.log(value);
    }
  } catch {
    // Fehler wird bereits über reader.closed behandelt
  }
};

lesen();
Teil 1 Stream-Fehler: Verbindung abgebrochen

Download-Ende bei fetch() erkennen

const ladeMitFortschritt = async (url) => {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let bytes = 0;

  reader.closed
    .then(() => console.log(`Download abgeschlossen: ${bytes} Bytes`))
    .catch((err) => console.error('Download fehlgeschlagen:', err));

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    bytes += value.byteLength;
  }
};

ladeMitFortschritt('https://example.com/');
Download abgeschlossen: 1256 Bytes

Ablehnung nach releaseLock()

const stream = new ReadableStream({
  start(controller) {
    controller.enqueue('Daten');
  }
});

const reader = stream.getReader();

reader.closed.catch((err) => {
  console.log(err.name); // Sperre wurde freigegeben
});

reader.releaseLock();

// Der Stream ist wieder frei und kann neu gesperrt werden
const neuerReader = stream.getReader();
console.log(stream.locked);
true TypeError

// Wichtig · Fallstricke

  • Unbehandelte Ablehnungen: Schlägt der Stream fehl oder wird die Sperre freigegeben, wird closed abgelehnt. Das passiert auch dann, wenn der Code das Promise nie benutzt. Wer closed abfragt, sollte deshalb immer einen .catch()-Handler anhängen. Sonst erscheinen Warnungen wegen unbehandelter Ablehnungen (unhandledrejection) in der Konsole bzw. in Node.js.
  • Keine Daten im Promise: closed wird mit undefined erfüllt. Die Daten selbst muss man weiterhin über read() lesen.
  • Wartende Chunks: Nach controller.close() ist der Stream erst dann geschlossen, wenn alle Chunks in der Warteschlange gelesen wurden. Wer nie liest, wartet also unter Umständen ewig auf closed.
  • Abbruch: Nach reader.cancel() wird closed erfüllt, nicht abgelehnt.
  • Kompatibilität: Wird von allen modernen Browsern unterstützt (Chrome, Edge, Firefox, Safari), außerdem in Web Workers und in Node.js über stream/web bzw. global ab Node.js 18. Das gleichnamige Promise gibt es auch bei ReadableStreamBYOBReader.closed und WritableStreamDefaultWriter.closed.