Start · Sprachen · JavaScript · Referenz · ReadableStreamBYOBReader.cancel()

ReadableStreamBYOBReader.cancel()

Methode

Bricht den zugrunde liegenden Byte-Stream ab und gibt ein Promise zurück, das erfüllt wird, sobald der Abbruch abgeschlossen ist.

seit JavaScript Baseline 2024: Chrome 89+, Edge 89+, Firefox 102+, Kategorie: webapi

Signatur

ReadableStreamBYOBReader.prototype.cancel(reason)

Beschreibung

Mit cancel() teilt ein Konsument mit, dass er an den weiteren Daten eines Byte-Streams kein Interesse mehr hat. Der Aufruf leitet den Abbruch an die Quelle weiter: Bei einem selbst erzeugten ReadableStream mit type: "bytes" wird der cancel(reason)-Callback der zugrunde liegenden Quelle aufgerufen, bei einem fetch()-Body wird die Netzwerkverbindung abgebaut.

Der Aufruf wirkt genauso wie ReadableStream.prototype.cancel(), nur eben über den Reader, der den Stream gerade gesperrt hält. Das ist praktisch, weil ein gesperrter Stream sich nicht direkt über stream.cancel() abbrechen lässt. Das schlägt mit einem TypeError fehl. Solange der Reader die Sperre hält, führt der Abbruch also über ihn.

Nach dem Abbruch wird jede noch offene read()-Anfrage mit { done: true, value: undefined } abgeschlossen. Pufferinhalte in der Warteschlange werden verworfen. Das zurückgegebene Promise wird mit undefined erfüllt, sobald die Quelle ihren Abbruch fertig bearbeitet hat. Es wird abgelehnt, wenn der Stream bereits in einem Fehlerzustand ist oder die cancel-Logik der Quelle eine Ausnahme wirft.

Typische Einsätze sind: Lesen beenden, sobald genug Bytes da sind (zum Beispiel nur den Dateikopf auslesen), Abbruch durch Benutzer oder Zeitlimit und Aufräumen im Fehlerfall.

Parameter

Name Typ Default Beschreibung
$reason any undefined Optionaler Grund für den Abbruch, zum Beispiel ein String oder ein Error. Er wird an den cancel-Callback der zugrunde liegenden Quelle weitergereicht und ist nur für die Quelle bestimmt.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das mit undefined erfüllt wird, sobald der Stream abgebrochen ist. Es wird mit einem TypeError abgelehnt, wenn der Reader nicht mehr aktiv ist (nach releaseLock()), und mit dem gespeicherten Fehler, wenn der Stream sich im Fehlerzustand befindet.

Beispiele

Nur den Dateikopf einer Antwort lesen und dann abbrechen

// Liest die ersten 8 Bytes (z. B. die PNG-Signatur) und bricht den Download ab
const readHeader = async (url, size = 8) => {
  const response = await fetch(url);
  const reader = response.body.getReader({ mode: 'byob' });

  let buffer = new ArrayBuffer(size);
  let offset = 0;

  while (offset < size) {
    const { value, done } = await reader.read(
      new Uint8Array(buffer, offset, size - offset)
    );
    if (done) break;
    buffer = value.buffer; // Puffer wurde übertragen, neu übernehmen
    offset += value.byteLength;
  }

  // Restliche Daten werden nicht gebraucht, also Verbindung schließen
  await reader.cancel('Header gelesen');
  return new Uint8Array(buffer, 0, offset);
};

const header = await readHeader('/bilder/logo.png');
console.log([...header].map((b) => b.toString(16).padStart(2, '0')).join(' '));
89 50 4e 47 0d 0a 1a 0a

Grund des Abbruchs in einer eigenen Byte-Quelle empfangen

const stream = new ReadableStream({
  type: 'bytes',
  pull(controller) {
    controller.enqueue(new Uint8Array([1, 2, 3, 4]));
  },
  cancel(reason) {
    console.log('Quelle abgebrochen:', reason);
  }
});

const reader = stream.getReader({ mode: 'byob' });
const { value } = await reader.read(new Uint8Array(4));
console.log('Gelesen:', value);

await reader.cancel('Benutzer hat abgebrochen');
console.log('Abbruch abgeschlossen');

// Nach dem Abbruch meldet read() sofort das Ende
const result = await reader.read(new Uint8Array(4));
console.log(result.done);
Gelesen: Uint8Array(4) [1, 2, 3, 4] Quelle abgebrochen: Benutzer hat abgebrochen Abbruch abgeschlossen true

Lesen mit Zeitlimit abbrechen

const readWithTimeout = async (stream, ms) => {
  const reader = stream.getReader({ mode: 'byob' });
  const timer = setTimeout(() => {
    reader.cancel(new Error(`Zeitlimit von ${ms} ms überschritten`));
  }, ms);

  let total = 0;
  let buffer = new ArrayBuffer(4096);
  try {
    while (true) {
      const { value, done } = await reader.read(new Uint8Array(buffer));
      if (done) break;
      total += value.byteLength;
      buffer = value.buffer;
    }
  } finally {
    clearTimeout(timer);
    reader.releaseLock();
  }
  return total;
};

const res = await fetch('/api/grosse-datei');
console.log('Bytes gelesen:', await readWithTimeout(res.body, 2000));
Bytes gelesen: 131072

// Wichtig · Fallstricke

  • Nur für Byte-Streams: Einen ReadableStreamBYOBReader gibt es nur für Streams mit type: "bytes" (zum Beispiel fetch-Bodies oder Blob.stream()). Bei anderen Streams wirft getReader({ mode: 'byob' }) einen TypeError.
  • Abbruch ist endgültig: Ein abgebrochener Stream lässt sich nicht fortsetzen. Wer die Daten später noch braucht, sollte vorher mit stream.tee() verzweigen.
  • Kein Fehler für Leser: cancel() versetzt den Stream in den Zustand „geschlossen“, nicht in den Fehlerzustand. Offene read()-Aufrufe liefern also done: true und werden nicht abgelehnt. Der reason kommt beim Leser nicht an.
  • Nach releaseLock(): Ein Reader, dessen Sperre freigegeben wurde, kann nicht mehr abbrechen. Das Promise wird dann mit TypeError abgelehnt. In dem Fall stattdessen stream.cancel() verwenden.
  • Übertragene Puffer: Beim BYOB-Lesen werden die übergebenen ArrayBuffer übertragen (detached). Nach einem Abbruch sollte man nicht mehr auf alte Views zugreifen.
  • Kompatibilität: Safari unterstützt BYOB-Reader erst seit Version 18.4. Bei Bedarf per typeof ReadableStreamBYOBReader !== 'undefined' prüfen und auf ReadableStreamDefaultReader zurückfallen. In Node.js sind die Web-Streams über node:stream/web bzw. global verfügbar.