Start · Sprachen · JavaScript · Referenz · Request.signal

Request.signal

Eigenschaft

Die schreibgeschützte Eigenschaft <code>signal</code> gibt das <code>AbortSignal</code> zurück, mit dem sich die zu einem <code>Request</code> gehörende Fetch-Operation abbrechen lässt.

seit JavaScript Baseline: alle modernen Browser (Chrome 66, Firefo Kategorie: webapi

Signatur

Request.signal

Beschreibung

Request.signal liefert das AbortSignal, das mit einem Request-Objekt verknüpft ist. Wird der Request per fetch() abgeschickt und das Signal ausgelöst, bricht der Browser die Netzwerkanfrage ab. Das von fetch() zurückgegebene Promise wird dann mit einer DOMException namens AbortError abgelehnt, bei AbortSignal.timeout() mit TimeoutError.

Das Signal wird im Konstruktor über die Option signal festgelegt, also new Request(url, { signal }). Gibt man keins an, hat die Eigenschaft trotzdem immer einen Wert: Sie enthält dann ein eigenes Signal, das im normalen Seitenkontext nie ausgelöst wird. Wichtig: request.signal ist nicht dasselbe Objekt wie das übergebene Signal. Es ist ein abhängiges Signal, das dem Original folgt. Wird das Original abgebrochen, wird auch request.signal abgebrochen, aber request.signal === controller.signal ergibt false.

Besonders nützlich ist die Eigenschaft in Service Workern: Dort zeigt event.request.signal an, ob die aufrufende Seite ihre Anfrage abgebrochen hat, zum Beispiel weil der Nutzer weggeklickt hat. Dieses Signal kann man an Folge-Anfragen oder aufwendige Berechnungen weitergeben, damit unnötige Arbeit abgebrochen wird. Außerdem hilft die Eigenschaft in Bibliotheken, die fertige Request-Objekte bekommen und prüfen wollen, ob diese schon abgebrochen wurden.

Rückgabewert

Typ
AbortSignal
Beschreibung
Das mit dem Request verknüpfte AbortSignal. Über aborted, reason und das Ereignis abort lässt sich sein Zustand abfragen.

Beispiele

Request mit AbortController abbrechen

const controller = new AbortController();
const request = new Request('https://jsonplaceholder.typicode.com/posts', {
  signal: controller.signal
});

console.log(request.signal.aborted); // false

const promise = fetch(request)
  .then((res) => res.json())
  .catch((err) => console.log(`${err.name}: Anfrage abgebrochen`));

// Sofort abbrechen, z. B. weil der Nutzer auf „Abbrechen“ klickt
controller.abort();
console.log(request.signal.aborted); // true
await promise;
false true AbortError: Anfrage abgebrochen

Zeitlimit mit AbortSignal.timeout()

const loadWithTimeout = async (url, ms) => {
  const request = new Request(url, { signal: AbortSignal.timeout(ms) });
  try {
    const res = await fetch(request);
    return await res.text();
  } catch (err) {
    if (err.name === 'TimeoutError') {
      console.warn(`Zeitüberschreitung nach ${ms} ms`);
      console.log(request.signal.reason.name);
      return null;
    }
    throw err;
  }
};

await loadWithTimeout('https://httpbin.org/delay/5', 1000);
Zeitüberschreitung nach 1000 ms TimeoutError

Service Worker: Abbruch des Clients weiterreichen

// sw.js
self.addEventListener('fetch', (event) => {
  const { request } = event;
  if (!request.url.includes('/api/report')) return;

  event.respondWith((async () => {
    // Bricht die Seite ab, wird auch die Upstream-Anfrage abgebrochen
    request.signal.addEventListener('abort', () => {
      console.log('Client hat die Anfrage abgebrochen');
    });
    return fetch(request.url, { signal: request.signal });
  })());
});

Signal ist ein abhängiges Objekt, kein identisches

const controller = new AbortController();
const request = new Request('/daten', { signal: controller.signal });

console.log(request.signal === controller.signal); // false
console.log(request.signal instanceof AbortSignal); // true

// Ohne signal-Option gibt es trotzdem ein Signal
const plain = new Request('/daten');
console.log(plain.signal.aborted); // false

// Signal einer Kopie ersetzen
const neu = new AbortController();
const kopie = new Request(request, { signal: neu.signal });
neu.abort();
console.log(kopie.signal.aborted, request.signal.aborted); // true false
false true false true false

// Wichtig · Fallstricke

  • Schreibgeschützt: request.signal = … hat keine Wirkung (im Strict Mode gibt es einen TypeError). Um ein anderes Signal zu verwenden, erstellt man einen neuen Request: new Request(request, { signal }).
  • Keine Identität: request.signal ist ein abhängiges Signal, nicht das übergebene Objekt. Vergleiche mit === schlagen daher fehl. Zustand und reason werden aber übernommen.
  • Signal von fetch() hat Vorrang: Bei fetch(request, { signal: other }) gilt other, nicht das Signal im Request.
  • Schon abgebrochen: Ist das Signal beim Aufruf von fetch() bereits ausgelöst, wird das Promise sofort abgelehnt. Wer ein Request-Objekt mehrfach verwenden will, sollte vorher request.signal.aborted prüfen.
  • Abbruch nach Eingang der Antwort: Wird erst abgebrochen, nachdem die Response da ist, schlägt das Lesen des Body fehl (res.json() usw. mit AbortError).
  • Kompatibilität: In allen modernen Browsern verfügbar. Dass event.request.signal im Service Worker beim Abbruch durch den Client wirklich ausgelöst wird, war lange uneinheitlich umgesetzt. Man sollte also nicht fest damit rechnen. In Node.js ist die Eigenschaft seit Version 18 über das eingebaute fetch (undici) verfügbar.