// js · Web Platform Advent #2
JavaScript Promises: ein praktischer Leitfaden
Verstehen Sie JavaScript-Promises — sie erstellen, then/catch/finally, Verkettung und das Kombinieren vieler mit Promise.all, race, any und allSettled — mit ausführbaren Beispielen und den häufigsten Fallstricken.
Ein Promise ist ein Objekt, das einen Wert darstellt, der noch nicht bereit ist. Es ist die Grundlage des asynchronen JavaScript: fetch(), Timer, Dateilesevorgänge in Node und die meisten modernen APIs geben Promises zurück. Ein Promise befindet sich immer in einem von drei Zuständen — ausstehend, erfüllt oder abgelehnt — und sobald es entschieden ist, ändert es sich nie wieder.
Ein Promise erstellen
Meistens verwenden Sie Promises, die Ihnen eine API übergibt. Aber Sie erstellen selbst eines mit dem Promise-Konstruktor, der eine Executor-Funktion entgegennimmt, die resolve und reject erhält:
const wait = (ms) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
const fetchUser = (id) => new Promise((resolve, reject) => {
if (!id) reject(new Error('id is required'));
else resolve({ id, name: 'Ada' });
}); Der Aufruf von resolve(value) erfüllt das Promise mit diesem Wert; der Aufruf von reject(error) lehnt es ab. Alles, was synchron innerhalb des Executors geworfen wird, lehnt das Promise ebenfalls ab.
then, catch und finally
Sie lesen das Ergebnis eines Promise mit .then() für Erfolg, .catch() für Fehlschlag und .finally() für Aufräumarbeiten, die in beiden Fällen laufen:
fetchUser(42)
.then((user) => console.log('Got', user.name))
.catch((err) => console.error('Failed:', err.message))
.finally(() => console.log('Done')); .then() kann zwei Argumente annehmen — onFulfilled und onRejected — aber ein eigenes .catch() am Ende der Kette ist klarer und fängt zudem Fehler ab, die in früheren .then()-Handlern geworfen wurden.
Verkettung: die eigentliche Superkraft
Jedes .then() gibt ein neues Promise zurück, daher lassen sie sich verketten. Was immer Sie aus einem Handler zurückgeben, wird zur Eingabe des nächsten. Entscheidend ist: Wenn Sie ein Promise zurückgeben, wartet die Kette, bis es entschieden ist, bevor sie fortfährt:
fetch('/api/user')
.then((res) => res.json()) // returns a promise → chain waits
.then((user) => fetch('/api/posts?user=' + user.id))
.then((res) => res.json())
.then((posts) => console.log(posts.length, 'posts'))
.catch((err) => console.error(err)); Ein einzelnes .catch() am Ende behandelt eine Ablehnung aus jedem Schritt darüber, weshalb Verkettung das Verschachteln von Callbacks schlägt.
Promises gemeinsam ausführen
Wenn Aufgaben nicht voneinander abhängen, starten Sie sie parallel und kombinieren die Ergebnisse. Vier statische Methoden decken die häufigsten Fälle ab:
Promise.all([...])— wartet, bis alle erfüllt sind; wird abgelehnt, sobald eines abgelehnt wird. Gibt ein Array der Ergebnisse in Reihenfolge zurück.Promise.allSettled([...])— wartet, bis alle fertig sind, und wird nie abgelehnt; gibt ein Array von{ status, value }- oder{ status, reason }-Objekten zurück.Promise.race([...])— wird mit dem ersten Promise entschieden, das sich entscheidet, ob es erfüllt oder abgelehnt wird.Promise.any([...])— wird mit dem ersten Promise entschieden, das erfüllt wird; wird nur abgelehnt, wenn alle abgelehnt werden.
// Fetch three resources at once, fail if any fails
const [user, posts, prefs] = await Promise.all([
fetch('/api/user').then((r) => r.json()),
fetch('/api/posts').then((r) => r.json()),
fetch('/api/prefs').then((r) => r.json()),
]);
// Same, but tolerate individual failures
const results = await Promise.allSettled([taskA(), taskB(), taskC()]);
results.forEach((r) => {
if (r.status === 'fulfilled') console.log('ok', r.value);
else console.warn('failed', r.reason);
}); Häufige Fallstricke
- Das return vergessen. Wenn Sie innerhalb eines
.then()ein Promise aufrufen, es aber nicht mitreturnzurückgeben, wartet die Kette nicht darauf — Sie verlieren Reihenfolge und Fehlerbehandlung. - Unbehandelte Ablehnungen. Ein Promise ohne
.catch()(oder eintry/catchumawait) erzeugt einunhandledrejection. Behandeln Sie Fehler immer am Ende der Kette. - Callbacks und Promises vermischen. Rufen Sie
resolvenicht zweimal auf und auch nicht sowohlresolveals auchreject— nur der erste Aufruf zählt; der Rest wird stillschweigend ignoriert. - Der Executor läuft sofort. Die Funktion, die Sie an
new Promise()übergeben, läuft synchron, sofort — nur die.then()-Callbacks werden aufgeschoben.
Schnellreferenz
| Ziel | Methode |
|---|---|
| Erfolg behandeln | .then(onFulfilled) |
| Fehlschlag behandeln | .catch(onRejected) |
| In beiden Fällen aufräumen | .finally(fn) |
| Alle müssen gelingen | Promise.all |
| Jedes Ergebnis sammeln | Promise.allSettled |
| Erstes, das sich entscheidet | Promise.race |
| Erstes, das gelingt | Promise.any |
Promises sind die Klempnerei unter async/await, das nur eine schönere Syntax über denselben Objekten ist. Sobald sich Verkettung und die vier Kombinatoren natürlich anfühlen, hört asynchrones JavaScript auf, einschüchternd zu sein.