Signatur
Beschreibung
Mit finally() registrierst du einen Callback, der immer läuft, sobald ein Promise abgeschlossen ist, ob erfolgreich oder mit Fehler. Er eignet sich für Aufräumarbeiten, die in beiden Fällen nötig sind: einen Ladeindikator ausblenden, eine Verbindung schließen, einen Button wieder aktivieren oder eine Sperre freigeben. So musst du denselben Code nicht in then() und catch() doppelt schreiben.
Der Callback bekommt keine Argumente. Er erfährt also weder den Ergebniswert noch den Ablehnungsgrund. Das ist so gewollt: finally() soll unabhängig vom Ergebnis sein. Das zurückgegebene Promise reicht den ursprünglichen Zustand weiter. Ein erfüllter Wert bzw. ein Fehler geht unverändert an die nächsten Glieder der Kette. Was der Callback zurückgibt, wird normalerweise ignoriert.
Es gibt zwei Ausnahmen: Wirft der Callback einen Fehler oder gibt er ein abgelehntes Promise zurück, wird das neue Promise mit diesem Grund abgelehnt. Gibt der Callback ein Promise zurück, wartet die Kette, bis es abgeschlossen ist, und übernimmt danach den ursprünglichen Wert. In async-Funktionen entspricht finally() dem finally-Block von try...catch...finally.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $onFinally | Function | undefined | Funktion, die aufgerufen wird, wenn das Promise abgeschlossen ist. Sie erhält keine Argumente. Ist onFinally keine Funktion, wird der Wert intern durch eine Durchreich-Funktion ersetzt. Das Verhalten entspricht dann then(onFinally, onFinally). |
Rückgabewert
onFinally einen Fehler wirft oder ein abgelehntes Promise zurückgibt, wird es mit diesem neuen Grund abgelehnt.Beispiele
Ladeindikator nach fetch immer ausblenden
const loadUser = (id) => {
const spinner = document.querySelector('#spinner');
spinner.hidden = false;
return fetch(`/api/users/${id}`)
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
.catch((err) => {
console.error('Laden fehlgeschlagen:', err.message);
return null;
})
.finally(() => {
// läuft bei Erfolg UND bei Fehler
spinner.hidden = true;
console.log('Spinner ausgeblendet');
});
};
loadUser(42).then((user) => console.log('Ergebnis:', user));
Wert und Fehler werden unverändert durchgereicht
Promise.resolve('Daten')
.finally(() => 'wird ignoriert')
.then((value) => console.log('Wert:', value));
Promise.reject(new Error('Kaputt'))
.finally(() => console.log('Aufräumen'))
.catch((err) => console.log('Fehler:', err.message));
Fehler im finally-Callback überschreibt das Ergebnis
Promise.resolve(1)
.finally(() => {
throw new Error('Cleanup fehlgeschlagen');
})
.then((v) => console.log('Wert:', v))
.catch((err) => console.log('Abgelehnt:', err.message));
Asynchrones Aufräumen – die Kette wartet
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const withConnection = async () => {
const conn = { close: async () => { await sleep(100); console.log('Verbindung geschlossen'); } };
return Promise.resolve('Abfrage-Ergebnis')
.finally(() => conn.close()); // Kette wartet auf close()
};
const result = await withConnection();
console.log(result);
// Wichtig · Fallstricke
- Keine Argumente: Der Callback erhält weder den Wert noch den Fehler. Brauchst du einen davon, verwende
then()bzw.catch(). - Fehler werden nicht abgefangen:
finally()behandelt keine Ablehnung. Ohne ein folgendescatch()bleibt eine Ablehnung unbehandelt und löstunhandledrejectionaus. - Rückgabewert wird ignoriert:
returnim Callback ändert das Ergebnis nicht. Anders ist es beithrowoder einem abgelehnten Promise: Beides überschreibt den ursprünglichen Wert bzw. Fehler. Der ursprüngliche Fehler geht dabei verloren. - Unterschied zu
then(f, f): Dort ersetzt der Rückgabewert vonfdas Ergebnis, und ein Fehler gilt nach dem Aufruf als behandelt. Beifinally()ist beides nicht so. - Immer asynchron: Auch bei einem bereits abgeschlossenen Promise läuft der Callback erst als Microtask, nie synchron.
- Kompatibilität: Seit ES2018 in allen modernen Browsern (Chrome 63, Firefox 58, Safari 11.1, Edge 18) und in Node.js ab Version 10. Der Internet Explorer unterstützt die Methode nicht; dort ist ein Polyfill nötig, z. B. aus core-js.