Start · Sprachen · JavaScript · Referenz · WritableStreamDefaultController.signal

WritableStreamDefaultController.signal

Eigenschaft

Gibt das schreibgeschützte <code>AbortSignal</code> des Controllers zurück. Es wird ausgelöst, sobald der <code>WritableStream</code> abgebrochen wird.

seit JavaScript Baseline 2023: Chrome 98, Edge 98, Firefox 100, Sa Kategorie: webapi

Signatur

WritableStreamDefaultController.signal

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

Typ
AbortSignal
Beschreibung
Das mit dem Controller verknüpfte 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);
Schreiben fehlgeschlagen: Nutzer hat abgebrochen Senke abgebrochen: Nutzer hat abgebrochen

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'));
Abgebrochen: 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);
Stopp bei 4

// Wichtig · Fallstricke

  • Zeitpunkt: Das Signal wird sofort beim Aufruf von abort() ausgelöst. Die Methode abort() der Senke läuft dagegen erst, wenn ein laufendes write() 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 mit controller.error() oder einer abgelehnten Promise aus write().
  • Bei close() wird das Signal nicht ausgelöst, nur bei abort().
  • Listener mit { once: true } registrieren oder wieder entfernen. Sonst sammeln sich bei vielen write()-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 ist controller.signal gleich undefined, deshalb vorher prüfen, z. B. mit controller.signal?.aborted.