AbortController - abort() Methhode
Die abort() Methode des AbortController ist ein zentraler Bestandteil moderner Webentwicklung, der präzise Kontrolle über asynchrone Prozesse ermöglicht. Diese Methode sendet ein Abbruchsignal an alle verbundenen Operationen, wodurch Netzwerkanfragen, Dateiübertragungen oder andere langläufige Prozesse elegant beendet werden können. Als effektives Werkzeug zur Ressourcenoptimierung verhindert sie unnötige Verarbeitung und trägt zu einer verbesserten Anwendungsperformance bei.
Inhaltsverzeichnis
Einführung
Die abort()
Methode des AbortController
wird aufgerufen, um das Abbruchsignal an alle Operationen zu senden, die das zugehörige AbortSignal
beobachten.
Syntax
Die Syntax sieht wie folgt aus.
const controller = new AbortController();
controller.abort([reason]);
Parameter
reason
: (Optional) Ein beliebiger Wert, der angibt, warum die Operation abgebrochen wurde. Dieser Wert wird derreason
Eigenschaft desAbortSignal
Objekts zugewiesen und steht der abgebrochenen Operation zur Verfügung. Wenn nicht angegeben, ist derreason
einDOMException
mit dem NamenAbortError
.
Rückgabewert
undefined
Funktionsweise
Wenn abort()
aufgerufen wird:
- Die
aborted
Eigenschaft des zugehörigenAbortSignal
wird auftrue
gesetzt. - Das
abort
Ergebnis wird auf demAbortSignal
ausgelöst. Wenn einonabort
Handler für das Signal registriert wurde, wird dieser ausgeführt. - Asynchrone Operationen, die dieses Signal beobachten (z.B. ein
fetch
Aufruf), werden abgebrochen. Typischerweise führt dies dazu, dass die Promise der Operation mit einemDOMException
namesAbortError
rejected wird.
Einmal abgebrochen, kann ein AbortSignal
nicht “un-abgebrochen” werden. Um eine neue abbrechbare Operation zu starten, muss ein neuer AbortController
erstellt werden.
Beispiel 1
In diesem Beispiel bauen wir eine einfache HTML-Datei auf, welche per JavaScript Daten laden soll. Dabei werden wir die abort()
Methode verwenden, um eine Anfrage abzubrechen.
<!DOCTYPE html>
<html>
<head>
<title>AbortController - Einfaches Fetch-Beispiel</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<button id="fetch_button">Daten laden</button>
<button id="cancel_button">Laden abbrechen</button>
<div id="output"></div>
<script>
const buttonFetch = document.getElementById("fetch_button");
const buttonCancel = document.getElementById("cancel_button");
const outputContainer = document.getElementById("output");
let controller;
buttonFetch.addEventListener("click", async () => {
// Erstelle neuen AbortController
controller = new AbortController();
const signal = controller.signal;
outputContainer.textContent = "Lade Daten ...";
buttonCancel.disabled = false;
buttonFetch.disabled = true;
try {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1?_delay=5000", { signal });
if (!response.ok) {
throw new Error(`HTTP-Fehler. Status: ${response.status}`);
}
const data = await response.json();
outputContainer.textContent = `Daten geladen: ${JSON.stringify(data)}`;
} catch (error) {
if (error.name === "AbortError") {
outputContainer.textContent = "Fetch-Anfrage wurde abgebrochen";
console.log("Fetch abgebrochen");
} else {
outputContainer.textContent = `Fehler beim Laden: ${error.message}`;
console.log("Fetch Fehler:", error);
}
} finally {
buttonCancel.disabled = true;
buttonFetch.disabled = false;
}
});
buttonCancel.addEventListener("click", () => {
if (controller) {
controller.abort();
console.log("Abbruch angefordert");
}
});
buttonCancel.disabled = true;
</script>
</body>
</html>
Was haben wir als Ergebnis? Zwei Buttons im Browser und unseren Container, welcher verschiedene Zustände als Text ausgibt.
Wenn wir auf den Button “Daten laden” klicken und, während Daten geladen werden, auf den Button “Laden abbrechen” klicken, sehen wir dass unser Request abgebrochen wird.
Wenn wir den Request neustarten und diesen nicht abbrechen, werden die Daten regulär geladen.
Beispiel 2
Im nächsten Beispiel bauen wir eine HTML-Datei mit JavaScript auf, welche fetch
Funktion (Fetch API) verwendet, um Anfragen mit einer bestimmten Verzögerung zu starten.
Wir verwenden Timeout und AbortController
, um eine Anfrage abzubrechen, falls diese länger, als die übergebe Anzahl von Millisekunden dauert.
<!DOCTYPE html>
<html>
<head>
<title>AbortController - Beispiel 2</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>Beispiel: Fetch von Daten mit Timeout</h1>
<p>Öffne die Entwicklertools, um die Ausgabe zu sehen.</p>
<script>
async function fetchDataWithTimeout(url, timeoutMilliseconds) {
const controller = new AbortController();
const signal = controller.signal;
const timeoutId = setTimeout(() => {
controller.abort(new Error("Timeout überschritten"));
}, timeoutMilliseconds);
try {
console.log(`Starte Fetch für: ${url}`);
const response = await fetch(url, { signal, cache: "no-store" });
// Wenn der Fetch erfolgreich war, bevor der Timeout eintritt
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP Fehler! Status: ${response.status}`);
}
const data = await response.json();
console.log("Daten erfolgreich geladen:", data);
return data;
} catch (error) {
if (error.name === "AbortError") {
console.log("Fetch abgebrochen");
console.log("Grund:", signal.reason);
} else {
console.log("Anderer Fehler:", error);
}
// Wichtig: clearTimeout auch im Fehlerfall,
// falls der Fehler nicht durch den Timeout ausgelöst wurde
// und der Timeout noch laufen könnte
clearTimeout(timeoutId);
throw error;
}
}
// Verwendung
fetchDataWithTimeout("https://dummyjson.com/products/?limit=10&delay=3000", 2000)
.then(data => console.log("Verarbeitet:", data))
.catch(error => console.log("Endgültiger Fehler im Aufrufer gefangen."));
fetchDataWithTimeout("https://dummyjson.com/posts/?limit=5&delay=1000", 2000)
.then(data => console.log("Verarbeitet (Album):", data))
.catch(error => console.log("Endgültiger Fehler im Aufrufer gefangen (Album)"));
</script>
</body>
</html>
Wenn wir nun diese Datei im Browser öffnen und in die Konsole hineinschauen, werden wir feststellen, dass ein Request korrekt verarbeitet wurde und der andere in einen Fehler hineingelaufen ist.
Was passiert nun schrittweise in diesem Beispiel?
fetchDataWithTimeout("https://dummyjson.com/products/?limit=10&delay=3000", 2000)
fetchDataWithTimeout("https://dummyjson.com/posts/?limit=5&delay=1000", 2000)
- Beide Requests laufen gleichzeitig an
- Timeout ist bei beiden 2000 ms (2 Sekunden)
- Verzögerungen
- Produkte: 3000 ms (3 Sekunden)
- Posts: 1000 ms (1 Sekunde)
Das sehen wir auch an den Logs.
Starte Fetch für: https://dummyjson.com/products/?limit=10&delay=3000
Starte Fetch für: https://dummyjson.com/posts/?limit=5&delay=1000
Die nächsten Logs (3 und 4) zeigen, dass bestimmte Daten verarbeitet worden sind.
Daten erfolgreich geladen: {posts: Array(5), total: 251, skip: 0, limit: 5}
Verarbeitet (Posts): {posts: Array(5), total: 251, skip: 0, limit: 5}
- Der
posts
Request mit 1s Verzögerung wird rechtzeitig fertig. - Im
try
Block vonfetchDataWithTimeout
:clearTimeout
wird aufgerufen, da Antwort rechtzeitig kamresponse.ok
isttrue
- Die Daten werden erfolgreich geparst
- Das Ergebnis wird im
.then()
des Aufrufers ausgegeben. - Keine Fehler, alles sauber.
Anderer Fehler: Error: Timeout überschritten
at beispiel_2.html:19:22
- Der
products
Request mit 3s Verzögerung überschreitet den Timeout von 2s. - Nach 2 Sekunden wird
controller.abort()
aufgerufen. - Im
catch
Block der Funktion wird der Fehler erkannt, hier aber nicht alsAbortError
, sondern als allgemeiner Fehler mit der NachrichtTimeout überschritten
. - Dieser Fehler wird geloggt.
clearTimeout
wird trotzdem ausgeführt.- Fehler wird weitergeworfen (
throw error
).
Endgültiger Fehler im Aufrufer gefangen.
- Im
.catch()
des Aufrufers wird der Fehler aus demproducts
Fetch gefangen. - Dort wird eine einfache Meldung ausgegeben.