Signatur
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
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');
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]);
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);