Promise.allSettled – Alle Promises im Blick behalten
In modernen JavaScript-Anwendungen ist es gängige Praxis, mehrere asynchrone Vorgänge gleichzeitig auszuführen – etwa das Laden von Daten aus verschiedenen APIs, das Hochladen mehrerer Dateien oder das Einholen unterschiedlicher Konfigurationswerte. Oft möchte man warten, bis alle diese Vorgänge abgeschlossen sind, um anschließend eine Entscheidung zu treffen oder einen Anzeigestatus im UI zu aktualisieren. Hier kommt Promise.allSettled()
ins Spiel.
Was ist Promise.allSettled()
?
Promise.allSettled()
wurde mit ES2020 eingeführt und gibt dir eine komfortable Möglichkeit, den Ausgang mehrerer Promises gleichzeitig im Blick zu behalten. Anders als Promise.all()
, das bereits beim ersten Fehler abbricht, liefert Promise.allSettled()
immer dann ein Ergebnis, wenn alle übergebenen Promises entweder erfüllt (fulfilled) oder abgelehnt (rejected) wurden.
Der Rückgabewert von Promise.allSettled()
ist ein Promise, das sich erfüllt, sobald alle Promises abgearbeitet sind. Das Ergebnis ist ein Array von Objekten, wobei jedes Objekt zwei Eigenschaften hat:
- status:
'fulfilled'
oder'rejected'
- value: Der Wert, falls das Promise erfüllt wurde
- reason: Der Fehlergrund, falls das Promise abgelehnt wurde
Ein klassisches Beispiel:
const promise1 = Promise.resolve('Daten von Server A');
const promise2 = Promise.resolve('Daten von Server B');
const promise3 = Promise.reject('Fehler beim Abrufen von Server C');
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
console.log(results);
// [
// { status: 'fulfilled', value: 'Daten von Server A' },
// { status: 'fulfilled', value: 'Daten von Server B' },
// { status: 'rejected', reason: 'Fehler beim Abrufen von Server C' }
// ]
// Hier sieht man: Auch wenn eine Operation scheitert, erhältst du dennoch
// die Resultate aller anderen Promises. So kannst du gezielt auf Teilfehler reagieren.
});
Unterschied zu Promise.all()
Promise.all()
: Sobald eines der Promises fehlschlägt, wird ein Fehler geworfen und du erfährst nichts über die bereits erfolgreich erfüllten Promises.Promise.allSettled()
: Alle Promises werden abgewartet. Auch bei Fehlern bekommst du den Status aller ausgeführten Operationen und kannst sie entsprechend verarbeiten.
Praktische Anwendungsfälle
Promise.allSettled()
ist besonders dann nützlich, wenn nicht alle asynchronen Vorgänge gleichermaßen kritisch sind oder du im Fehlerfall zumindest einen Teil der Ergebnisse weiterverwenden möchtest. Untenstehend findest du mehrere Szenarien, die zeigen, wie Promise.allSettled()
in der Praxis helfen kann.
1. Teilweise verfügbare Daten von mehreren APIs abrufen
Du möchtest Daten aus verschiedenen Regionen laden, um sie in deiner Anwendung anzuzeigen. Ist eine Region nicht verfügbar, sollen die anderen trotzdem genutzt werden.
const regionEN = fetch('/api/products?lang=en').then(res => res.json());
const regionDE = fetch('/api/products?lang=de').then(res => res.json());
const regionFR = fetch('/api/products?lang=fr').then(res => res.json());
Promise.allSettled([regionEN, regionDE, regionFR])
.then(results => {
const successfulData = [];
const errors = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
successfulData.push(result.value);
} else {
const region = index === 0 ? 'EN' : index === 1 ? 'DE' : 'FR';
errors.push(`Daten für Region ${region} konnten nicht geladen werden: ${result.reason}`);
}
});
console.log('Erfolgreiche Ergebnisse:', successfulData);
console.log('Fehler:', errors);
// Trotz Fehlern bei einzelnen Regionen kannst du zumindest die verfügbaren Daten anzeigen.
});
2. Mehrere Dateien hochladen
Stell dir ein Upload-Formular vor, bei dem der Nutzer mehrere Dateien auf einmal hochladen kann. Manche Uploads könnten fehlschlagen, doch du möchtest zumindest die erfolgreichen Uploads weiterverarbeiten.
function uploadFile(file) {
// Simulierte Upload-Funktion mit 70% Erfolgswahrscheinlichkeit
return new Promise((resolve, reject) => {
const isSuccess = Math.random() > 0.3;
setTimeout(() => {
if (isSuccess) {
resolve(`Upload erfolgreich: ${file.name}`);
} else {
reject(`Upload fehlgeschlagen: ${file.name}`);
}
}, 500);
});
}
const files = [{ name: 'bild1.jpg' }, { name: 'bild2.png' }, { name: 'video.mp4' }];
const uploadPromises = files.map(f => uploadFile(f));
Promise.allSettled(uploadPromises)
.then(results => {
const successfulUploads = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failedUploads = results.filter(r => r.status === 'rejected').map(r => r.reason);
console.log('Erfolgreich hochgeladen:', successfulUploads);
console.log('Fehlgeschlagen:', failedUploads);
// So kann der Nutzer informiert werden, welche Dateien erfolgreich waren
// und welche erneut hochgeladen werden müssen.
});
3. Datenverarbeitung mit teilweise fehlerhaften Eingaben
Du lädst unterschiedliche Konfigurationen aus diversen Quellen. Nicht alle sind zuverlässig, aber du willst trotzdem mit den gültigen Teilkonfigurationen weiterarbeiten.
const config1 = fetch('/api/config1').then(res => res.json());
const config2 = fetch('/api/config2').then(res => res.json());
const config3 = fetch('/api/config3').then(res => res.json());
Promise.allSettled([config1, config2, config3])
.then(results => {
const validConfigs = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
if (validConfigs.length > 0) {
const mergedConfig = Object.assign({}, ...validConfigs);
console.log('Zusammengeführte Konfiguration:', mergedConfig);
} else {
console.warn('Keine gültigen Konfigurationen gefunden. Verwende Standardwerte.');
}
// Auch wenn einzelne Configs fehlen, arbeitest du einfach mit denen, die vorhanden sind.
});
4. UI-Updates nach mehreren asynchronen Aktionen
Deine App führt beim Start mehrere asynchrone Vorgänge gleichzeitig aus (Daten laden, Berechtigungen prüfen, Einstellungen einholen). Du möchtest erst dann den Ladebildschirm ausblenden, wenn alle Vorgänge abgeschlossen sind, selbst wenn einige fehlschlagen.
const loadUserData = fetch('/api/user').then(r => r.json());
const loadSettings = fetch('/api/settings').then(r => r.json());
const checkPermissions = fetch('/api/permissions').then(r => r.json());
Promise.allSettled([loadUserData, loadSettings, checkPermissions])
.then(results => {
document.getElementById('loading').style.display = 'none'; // Ladebildschirm ausblenden
const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failed = results.filter(r => r.status === 'rejected');
console.log('Erfolgreich geladen:', successful);
if (failed.length > 0) {
console.warn('Einige Aktionen sind fehlgeschlagen:', failed.map(f => f.reason));
// Eventuell Fallback-Daten oder Fehlermeldungen anzeigen
}
});
Fazit
Mit Promise.allSettled()
kannst du sicherstellen, dass du einen vollständigen Überblick über alle ausgeführten Promises behältst – egal, ob sie erfolgreich sind oder scheitern. Das ist besonders hilfreich in Anwendungsfällen, in denen du zumindest teilweise Ergebnisse weiterverarbeiten möchtest, anstatt beim ersten Fehler alles abzubrechen. Ob beim Laden von Daten aus verschiedenen Quellen, beim Hochladen von Dateien oder beim Zusammenführen von Konfigurationen: Promise.allSettled()
ermöglicht robustes Fehler- und Ergebnis-Handling, das deine Anwendung widerstandsfähiger macht und ein besseres Nutzererlebnis liefert.
Setze den Promise.allSettled()
in deinem nächsten Projekt ein, um noch mehr Nutzen aus deinen parallelen asynchronen Aktionen zu ziehen!