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

ReadableStreamDefaultReader.cancel()

Methode

Bricht den zugehörigen <code>ReadableStream</code> über den Reader ab und gibt ein Promise zurück, das sich nach dem Abbruch erfüllt.

seit JavaScript Baseline: alle modernen Browser (seit 2019/2020), Kategorie: webapi

Signatur

ReadableStreamDefaultReader.prototype.cancel(reason)

Beschreibung

Mit cancel() teilt der Verbraucher eines Streams mit, dass er keine weiteren Daten mehr benötigt. Der Aufruf entspricht ReadableStream.prototype.cancel(), wird aber über einen bereits gesperrten Reader ausgelöst. Die zugrunde liegende Quelle erhält den übergebenen Grund (reason) in ihrer cancel(reason)-Callback-Funktion und kann dort Ressourcen freigeben, z. B. eine Netzwerkverbindung schließen oder einen Timer stoppen.

Nach dem Abbruch werden alle noch in der internen Warteschlange liegenden Chunks verworfen. Offene read()-Aufrufe erfüllen sich mit { value: undefined, done: true }, und das closed-Promise des Readers wird erfüllt. Der Stream befindet sich danach im Zustand „geschlossen“.

Typische Einsatzfälle: Sie lesen eine große fetch()-Antwort nur so weit, bis Sie die gesuchte Information gefunden haben, der Nutzer bricht einen Download ab, oder ein Zeitlimit ist überschritten. Wichtig ist der Unterschied zu releaseLock(): releaseLock() gibt nur die Sperre frei, damit ein anderer Reader weiterlesen kann – cancel() beendet den Stream endgültig.

Parameter

Name Typ Default Beschreibung
$reason any undefined Frei wählbarer Abbruchgrund (z. B. ein String oder ein Error), der an die cancel()-Callback-Funktion der zugrunde liegenden Quelle weitergereicht wird.

Rückgabewert

Typ
Promise<undefined>
Beschreibung
Ein Promise, das sich mit undefined erfüllt, sobald der Stream abgebrochen wurde. Es wird abgelehnt, wenn der Reader nicht mehr an einen Stream gebunden ist (TypeError), der Stream bereits fehlerhaft ist oder die cancel()-Callback-Funktion der Quelle einen Fehler wirft bzw. ein abgelehntes Promise liefert.

Beispiele

Download abbrechen, sobald genug Daten gelesen wurden

const readFirstBytes = async (url, limit) => {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let received = 0;
  const chunks = [];

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    chunks.push(value);
    received += value.byteLength;
    if (received >= limit) {
      // Restliche Daten werden nicht mehr benötigt
      await reader.cancel('Limit erreicht');
      break;
    }
  }
  console.log(`Gelesen: ${received} Bytes`);
  return chunks;
};

readFirstBytes('https://example.com/', 1024);
Gelesen: 1256 Bytes

Abbruchgrund in der Quelle auswerten und aufräumen

let timer;
const stream = new ReadableStream({
  start(controller) {
    let i = 0;
    timer = setInterval(() => controller.enqueue(`Tick ${i++}`), 100);
  },
  cancel(reason) {
    clearInterval(timer); // Ressourcen freigeben
    console.log('Quelle abgebrochen:', reason);
  }
});

const reader = stream.getReader();
const { value } = await reader.read();
console.log(value);
await reader.cancel('Nutzer hat abgebrochen');
console.log(await reader.read());
Tick 0 Quelle abgebrochen: Nutzer hat abgebrochen { value: undefined, done: true }

Lesen mit Zeitlimit

const readWithTimeout = async (stream, ms) => {
  const reader = stream.getReader();
  const timeout = setTimeout(() => {
    reader.cancel(new Error('Zeitüberschreitung')).catch(() => {});
  }, ms);

  try {
    let text = '';
    const decoder = new TextDecoder();
    while (true) {
      const { value, done } = await reader.read();
      if (done) break; // auch nach cancel() erreicht
      text += decoder.decode(value, { stream: true });
    }
    return text;
  } finally {
    clearTimeout(timeout);
    reader.releaseLock();
  }
};

const res = await fetch('https://example.com/');
console.log((await readWithTimeout(res.body, 5000)).length);
1256

// Wichtig · Fallstricke

  • Daten gehen verloren: Bereits eingereihte, aber noch nicht gelesene Chunks werden verworfen. Brauchen Sie sie noch, verwenden Sie stattdessen releaseLock().
  • Kein Fehlerzustand: cancel() schließt den Stream regulär – ein laufendes read() liefert done: true, keinen Fehler. Prüfen Sie ggf. selbst, ob der Abbruch gewollt war.
  • Nach releaseLock() wirft cancel() auf demselben Reader ein abgelehntes Promise mit TypeError.
  • Bei fetch()-Antworten beendet cancel() in der Regel die Netzwerkübertragung; für einen Abbruch vor dem Lesen ist ein AbortController meist besser geeignet.
  • Bei Streams mit tee() wird die Quelle erst abgebrochen, wenn beide Zweige abgebrochen wurden.
  • Kompatibilität: In allen aktuellen Browsern verfügbar; in Node.js global ab Version 18 (vorher über stream/web).