Signatur
Beschreibung
Die schreibgeschützte Eigenschaft signal eines WritableStreamDefaultController liefert ein AbortSignal. Der Stream bricht dieses Signal ab, sobald jemand WritableStream.prototype.abort() oder WritableStreamDefaultWriter.prototype.abort() aufruft. Einen neuen Controller muss man dafür nicht anlegen: Der Stream stellt den Controller selbst bereit und übergibt ihn an die Methoden start(), write() und close() der zugrunde liegenden Senke (underlying sink).
Wichtig ist das Signal vor allem bei lang laufenden Schreibvorgängen. Die Methode abort() der Senke ruft der Stream erst auf, wenn ein laufendes write() fertig ist. Das Signal dagegen wird sofort ausgelöst. So kann die Senke einen laufenden Vorgang direkt abbrechen, etwa einen fetch()-Upload, einen Timer oder eine Schreiboperation im Dateisystem. Dazu reicht man das Signal einfach an APIs weiter, die ein AbortSignal annehmen.
Über signal.reason erhält man den Grund, der an abort(reason) übergeben wurde. Mit signal.aborted prüft man synchron, ob der Stream schon abgebrochen wurde. Über das Ereignis abort lässt sich auf den Abbruch reagieren.
Rückgabewert
AbortSignal. Solange der Stream nicht abgebrochen wurde, ist aborted gleich false. Nach dem Abbruch ist aborted gleich true, und reason enthält den übergebenen Abbruchgrund.Beispiele
Laufenden fetch-Upload beim Abbruch des Streams stoppen
const uploadStream = new WritableStream({
async write(chunk, controller) {
// Signal an fetch weiterreichen: bricht die Anfrage sofort ab
await fetch('/api/upload', {
method: 'POST',
body: chunk,
signal: controller.signal,
});
},
abort(reason) {
console.log('Senke abgebrochen:', reason);
},
});
const writer = uploadStream.getWriter();
writer.write(new TextEncoder().encode('Teil 1')).catch((err) => {
console.log('Schreiben fehlgeschlagen:', err);
});
// Nach 100 ms abbrechen, z. B. weil der Nutzer auf „Abbrechen“ klickt
setTimeout(() => writer.abort('Nutzer hat abgebrochen'), 100);
Auf das abort-Ereignis reagieren und Timer aufräumen
const slowSink = new WritableStream({
write(chunk, controller) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
console.log('Geschrieben:', chunk);
resolve();
}, 1000);
controller.signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(controller.signal.reason);
}, { once: true });
});
},
});
const writer = slowSink.getWriter();
writer.write('Hallo').catch((e) => console.log('Abgebrochen:', e.message));
writer.abort(new Error('Timeout'));
Status synchron mit signal.aborted prüfen
const sink = new WritableStream({
async write(chunks, controller) {
for (const item of chunks) {
// Vor jedem Teilschritt prüfen, ob abgebrochen wurde
if (controller.signal.aborted) {
console.log('Stopp bei', item);
throw controller.signal.reason;
}
await new Promise((r) => setTimeout(r, 50));
}
},
});
const writer = sink.getWriter();
writer.write([1, 2, 3, 4, 5]).catch(() => {});
setTimeout(() => writer.abort('genug'), 120);
// Wichtig · Fallstricke
- Zeitpunkt: Das Signal wird sofort beim Aufruf von
abort()ausgelöst. Die Methodeabort()der Senke läuft dagegen erst, wenn ein laufendeswrite()fertig ist. Wer lange Vorgänge nicht an das Signal koppelt, blockiert den Abbruch. - Kein Ersatz für
controller.error(): Das Signal zeigt nur einen Abbruch von außen an. Eigene Fehler meldet man mitcontroller.error()oder einer abgelehnten Promise auswrite(). - Bei
close()wird das Signal nicht ausgelöst, nur beiabort(). - Listener mit
{ once: true }registrieren oder wieder entfernen. Sonst sammeln sich bei vielenwrite()-Aufrufen Listener an, und es entstehen Speicherlecks. - Kompatibilität: Unterstützt ab Chrome/Edge 98, Firefox 100, Safari 16.4 und Node.js 18 (
stream/web). In älteren Umgebungen istcontroller.signalgleichundefined, deshalb vorher prüfen, z. B. mitcontroller.signal?.aborted.