Start · Sprachen · JavaScript · Referenz · TransformStreamDefaultController.terminate()

TransformStreamDefaultController.terminate()

Methode

Beendet einen <code>TransformStream</code> vorzeitig: Die lesbare Seite wird geschlossen, auf der beschreibbaren Seite entsteht ein Fehler (<code>TypeError</code>).

seit JavaScript Baseline: Chrome 67, Firefox 102, Safari 14.1, Nod Kategorie: webapi

Signatur

TransformStreamDefaultController.prototype.terminate()

Beschreibung

Mit terminate() beendet ein TransformStream seine Arbeit von innen heraus, meist aus transform() oder start(). Die lesbare Seite wird ganz normal geschlossen. Chunks, die schon mit enqueue() eingereiht wurden, bleiben lesbar. Danach meldet der Leser done: true. Die beschreibbare Seite bekommt dagegen einen Fehler (TypeError). Weitere Schreibversuche schlagen deshalb fehl.

Das ist praktisch, wenn eine Transformation genug Daten hat und der Rest egal ist. Beispiele sind „nimm nur die ersten n Elemente“, „lies bis zu einer Endmarkierung“ oder „brich ab, sobald ein Treffer gefunden wurde“. Wurde der Stream mit pipeThrough() eingebunden, wird die Quelle durch den Fehler auf der beschreibbaren Seite automatisch abgebrochen (cancel). Bei einem fetch()-Body wird dann nichts mehr unnötig heruntergeladen.

Anders als controller.error() gilt terminate() für den Leser als sauberes Ende. Der Leser sieht keinen Fehler, und eingereihte Chunks gehen nicht verloren. error() setzt dagegen beide Seiten in den Fehlerzustand und verwirft alle Chunks in der Warteschlange.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert.

Beispiele

Nur die ersten n Chunks durchlassen

const takeFirst = (n) => {
  let count = 0;
  return new TransformStream({
    transform(chunk, controller) {
      controller.enqueue(chunk);
      count++;
      if (count >= n) controller.terminate(); // Rest wird verworfen
    }
  });
};

const source = new ReadableStream({
  start(controller) {
    for (const x of [1, 2, 3, 4, 5]) controller.enqueue(x);
    controller.close();
  }
});

const reader = source.pipeThrough(takeFirst(3)).getReader();
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  console.log(value);
}
console.log('fertig');
1 2 3 fertig

HTML-Download nach dem </head> abbrechen

const untilMarker = (marker) => {
  let buffer = '';
  return new TransformStream({
    transform(chunk, controller) {
      buffer += chunk;
      const idx = buffer.indexOf(marker);
      if (idx !== -1) {
        controller.enqueue(buffer.slice(0, idx + marker.length));
        controller.terminate(); // fetch-Body wird per cancel abgebrochen
      }
    },
    flush(controller) {
      // nur bei regulärem Ende ohne Treffer
      controller.enqueue(buffer);
    }
  });
};

const response = await fetch('https://example.com/');
const reader = response.body
  .pipeThrough(new TextDecoderStream())
  .pipeThrough(untilMarker('</head>'))
  .getReader();

let head = '';
for (let r = await reader.read(); !r.done; r = await reader.read()) {
  head += r.value;
}
console.log(head.match(/<title>(.*?)<\/title>/)?.[1]);
Example Domain

Verhalten auf der beschreibbaren Seite

const { readable, writable } = new TransformStream({
  transform(chunk, controller) {
    if (chunk === 'STOP') {
      controller.terminate();
      return;
    }
    controller.enqueue(chunk.toUpperCase());
  }
});

// Leser parallel starten (sonst blockiert der Gegendruck die Schreibvorgänge)
const collected = (async () => {
  const reader = readable.getReader();
  const out = [];
  for (let r = await reader.read(); !r.done; r = await reader.read()) {
    out.push(r.value);
  }
  return out;
})();

const writer = writable.getWriter();
await writer.write('hallo');
await writer.write('STOP');
try {
  await writer.write('welt');
} catch (err) {
  console.log('Schreibfehler:', err.name);
}
console.log('Gelesen:', await collected);
Schreibfehler: TypeError Gelesen: [ 'HALLO' ]