Start · Sprachen · JavaScript · Referenz · AbortController

AbortController

Schnittstelle

Das <code>AbortController</code>-Interface stellt ein Controller-Objekt bereit, mit dem sich eine oder mehrere asynchrone Operationen wie <code>fetch()</code> über ein <code>AbortSignal</code> abbrechen lassen.

seit JavaScript Baseline: alle modernen Browser (seit 2019), Node. Kategorie: webapi

Signatur

interface AbortController

Beschreibung

Mit AbortController lassen sich asynchrone Vorgänge von außen abbrechen. Ein Controller wird mit new AbortController() erzeugt und hat genau zwei Bestandteile: die schreibgeschützte Eigenschaft signal (ein AbortSignal) und die Methode abort(reason). Das Signal gibt man an die Operation weiter, die abbrechbar sein soll. Den Controller behält man selbst, um den Abbruch auszulösen.

Typische Einsatzfälle sind:

  • das Abbrechen von fetch()-Anfragen, z. B. wenn der Nutzer die Seite wechselt oder eine neue Suche startet,
  • das Abbrechen von Streams und vom Lesen eines Response-Bodys,
  • das Entfernen mehrerer Event-Listener auf einmal über die Option signal von addEventListener(),
  • der Abbruch eigener asynchroner Funktionen, die auf das Signal hören.

Ruft man abort() auf, wechselt signal.aborted auf true, das Signal löst das Ereignis abort aus und signal.reason bekommt den übergebenen Grund. Ohne Grund ist das eine DOMException mit dem Namen "AbortError". Laufende fetch()-Promises werden mit diesem Grund zurückgewiesen.

Ein Controller kann nur einmal abbrechen. Ein bereits abgebrochenes Signal bleibt dauerhaft abgebrochen. Für jeden neuen Vorgang, der sich separat abbrechen lassen soll, braucht man deshalb einen neuen AbortController.

Parameter

Name Typ Default Beschreibung
$reason any DOMException "AbortError" Nur bei abort(reason): der Abbruchgrund. Er steht danach in signal.reason und wird zum Grund, mit dem abhängige Promises zurückgewiesen werden.

Rückgabewert

Typ
AbortController
Beschreibung
Der Konstruktor new AbortController() liefert eine neue Instanz mit frischem, nicht abgebrochenem signal. abort() selbst gibt undefined zurück.

Beispiele

Fetch-Anfrage per Button abbrechen

const controller = new AbortController();
const { signal } = controller;

document.querySelector('#cancel').addEventListener('click', () => {
  controller.abort();
});

try {
  const response = await fetch('/api/grosse-datei', { signal });
  const data = await response.json();
  console.log('Geladen:', data);
} catch (err) {
  if (err.name === 'AbortError') {
    console.log('Anfrage wurde abgebrochen');
  } else {
    throw err;
  }
}
Anfrage wurde abgebrochen

Veraltete Suchanfragen verwerfen (Typeahead)

let controller = null;

const search = async (query) => {
  controller?.abort(); // vorherige Anfrage abbrechen
  controller = new AbortController();

  try {
    const res = await fetch(`/api/suche?q=${encodeURIComponent(query)}`, {
      signal: controller.signal,
    });
    return await res.json();
  } catch (err) {
    if (err.name === 'AbortError') return null; // überholt
    throw err;
  }
};

document.querySelector('#q').addEventListener('input', async (e) => {
  const result = await search(e.target.value);
  if (result) console.log(result);
});

Mehrere Event-Listener auf einmal entfernen

const controller = new AbortController();
const { signal } = controller;

window.addEventListener('resize', () => console.log('resize'), { signal });
window.addEventListener('scroll', () => console.log('scroll'), { signal });
document.addEventListener('keydown', (e) => console.log(e.key), { signal });

// Später, z. B. beim Entfernen einer Komponente:
controller.abort();
console.log(signal.aborted);
true

Eigene asynchrone Funktion abbrechbar machen, mit eigenem Grund

const wait = (ms, { signal } = {}) =>
  new Promise((resolve, reject) => {
    if (signal?.aborted) return reject(signal.reason);
    const id = setTimeout(resolve, ms, 'fertig');
    signal?.addEventListener('abort', () => {
      clearTimeout(id);
      reject(signal.reason);
    }, { once: true });
  });

const controller = new AbortController();
setTimeout(() => controller.abort(new Error('Vom Nutzer abgebrochen')), 500);

try {
  console.log(await wait(2000, { signal: controller.signal }));
} catch (err) {
  console.log(err.message);
}
Vom Nutzer abgebrochen

// Wichtig · Fallstricke

  • Nur einmal verwendbar: Nach abort() bleibt das Signal dauerhaft abgebrochen. Übergibt man es an ein neues fetch(), wird dieses sofort zurückgewiesen. Für jeden neuen Vorgang also einen neuen Controller anlegen.
  • Fehler unterscheiden: Ein Abbruch erscheint als abgewiesenes Promise. Prüfe err.name === 'AbortError', damit Abbrüche nicht als echte Fehler gemeldet werden. Mit eigenem reason wird genau dieser Wert geworfen, nicht der AbortError.
  • Kein Rückgängigmachen auf dem Server: Mit abort() hört der Client auf, auf die Antwort zu warten. Eine Anfrage, die schon beim Server angekommen ist (z. B. POST), wird dort unter Umständen trotzdem vollständig verarbeitet.
  • Zeitlimits: Für einfache Zeitlimits ist AbortSignal.timeout(ms) kürzer. Mehrere Signale kombiniert man mit AbortSignal.any([...]). Beide sind neuer und werden von älteren Browsern nicht unterstützt.
  • Kompatibilität: AbortController ist in allen modernen Browsern verfügbar. Der Parameter reason und signal.reason kamen erst um 2022 dazu (Chrome 98, Firefox 97, Safari 15.4). In Node.js ist AbortController ab Version 15 global verfügbar. Der Internet Explorer unterstützt es nicht.
  • Listener, die man selbst per signal.addEventListener('abort', …) registriert, sollten die Option { once: true } nutzen, damit bei langlebigen Signalen keine Speicherlecks entstehen.