Umgang mit mehreren API-Anfragen mit Promise.all und Promise.allSettled
James Reed
Infrastructure Engineer · Leapcell

Einleitung
In der modernen Webentwicklung ist es üblich, dass Anwendungen mit verschiedenen Backend-Diensten interagieren, was oft mehrere API-Aufrufe erfordert, um Daten abzurufen, Ressourcen zu aktualisieren oder komplexe Vorgänge auszuführen. Wenn es um eine Reihe solcher API-Anfragen geht, sind eine effiziente und robuste Handhabung von größter Bedeutung. Zwei leistungsstarke JavaScript Promise-Kombinatoren, Promise.all
und Promise.allSettled
, bieten unterschiedliche Ansätze zur Verwaltung dieser gleichzeitigen Vorgänge. Das Verständnis ihrer Nuancen ist für Entwickler entscheidend, um das richtige Werkzeug für die jeweilige Aufgabe auszuwählen und so die Stabilität der Anwendung und ein optimales Benutzererlebnis zu gewährleisten. Dieser Artikel befasst sich mit den praktischen Anwendungen dieser Methoden, untersucht ihre Kernfunktionalitäten, Unterschiede und leitet Sie an, wann Sie welche für eine effektive Verarbeitung von Batch-API-Anfragen verwenden sollten.
Kernkonzepte und praktische Anwendungen
Bevor wir uns mit den Einzelheiten befassen, wollen wir ein gemeinsames Verständnis der Kernkonzepte im Zusammenhang mit asymmetrischen Operationen in JavaScript, insbesondere Promises, schaffen.
Promises: Ein Promise ist ein Objekt, das die endgültige Erfüllung oder Ablehnung einer asynchronen Operation darstellt. Es hat drei Zustände:
- Pending: Anfangszustand, weder erfüllt noch abgelehnt.
- Fulfilled: Bedeutet, dass die Operation erfolgreich abgeschlossen wurde.
- Rejected: Bedeutet, dass die Operation fehlgeschlagen ist.
Nun wollen wir uns Promise.all
und Promise.allSettled
ansehen.
Promise.all erklärt
Promise.all
nimmt ein Iterable von Promises (z. B. ein Array von Promises) entgegen und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird aufgelöst, sobald alle Eingabe-Promises aufgelöst wurden, und zwar mit einem Array ihrer aufgelösten Werte, in der gleichen Reihenfolge wie die Eingabe-Promises. Es wird abgelehnt, sobald eines der Eingabe-Promises abgelehnt wird, mit dem Grund des ersten abgelehnten Promises.
Prinzip: "Alles oder nichts." Wenn auch nur eine Anfrage fehlschlägt, gilt der gesamte Batch-Vorgang als fehlgeschlagen. Dies ist ideal, wenn der Erfolg aller Anfragen voneinander abhängt.
Anwendungsfall:
Stellen Sie sich eine E-Commerce-Anwendung vor, bei der Sie gleichzeitig Produktdetails und Benutzerbewertungen abrufen müssen, bevor Sie eine Produktseite anzeigen. Wenn einer dieser API-Aufrufe fehlschlägt, wäre die Anzeige einer unvollständigen Produktseite nicht hilfreich oder sogar irreführend. In diesem Szenario möchten Sie, dass Promise.all
sicherstellt, dass beide Daten erfolgreich abgerufen werden.
Implementierungsbeispiel:
// Simulierte API-Aufrufe const fetchProductDetails = (productId) => { return new Promise((resolve, reject) => { setTimeout(() => { if (productId === 'p123') { resolve({ id: 'p123', name: 'Premium Widget', price: 29.99 }); } else { reject(new Error(`Produkt ${productId} nicht gefunden`)); } }, 1000); }); }; const fetchUserReviews = (productId) => { return new Promise((resolve, reject) => { setTimeout(() => { if (productId === 'p123') { resolve([ { user: 'Alice', rating: 5, comment: 'Tolles Produkt!' }, { user: 'Bob', rating: 4, comment: 'Gutes Preis-Leistungs-Verhältnis.' } ]); } else if (productId === 'p456') { // Simulieren einer fehlgeschlagenen Bewertungsanfrage reject(new Error('Fehler beim Abrufen der Bewertungen für Produkt p456')); } else { resolve([]); // Keine Bewertungen für andere Produkte } }, 800); }); }; const productId = 'p123'; // Beispiel: Erfolgreiches Szenario // const productId = 'p456'; // Beispiel: Eine Anfrage schlägt fehl Promise.all([ fetchProductDetails(productId), fetchUserReviews(productId) ]) .then(([productDetails, userReviews]) => { console.log("Promise.all Erfolg:"); console.log("Produktdetails:", productDetails); console.log("Benutzerbewertungen:", userReviews); // Produktseite mit beiden Daten rendern }) .catch(error => { console.error("Promise.all Fehlgeschlagen:", error.message); // Dem Benutzer eine Fehlermeldung anzeigen oder erneut versuchen }); // Beispiel mit einer fehlgeschlagenen Anfrage (zum Testen auskommentieren) /* const failingProductId = 'p456'; Promise.all([ fetchProductDetails(failingProductId), // Diese könnte erfolgreich sein fetchUserReviews(failingProductId) // Diese wird abgelehnt ]) .then(([productDetails, userReviews]) => { console.log("Promise.all Erfolg (sollte hier nicht ankommen):", productDetails, userReviews); }) .catch(error => { console.error("Promise.all mit Fehler:", error.message); // Ausgabe: Fehler beim Abrufen der Bewertungen für Produkt p456 // Der gesamte Batch ist fehlgeschlagen, weil eine Anfrage fehlgeschlagen ist. }); */
Promise.allSettled erklärt
Promise.allSettled
nimmt ebenfalls ein Iterable von Promises entgegen und gibt ein einzelnes Promise zurück. Dieses zurückgegebene Promise wird aufgelöst, sobald alle Eingabe-Promises entweder erfüllt oder abgelehnt wurden. Es wird mit einem Array von Objekten aufgelöst, die jeweils den Ausgang eines entsprechenden Promises beschreiben. Jedes Objekt hat eine status
-Eigenschaft (entweder 'fulfilled'
oder 'rejected'
) und entweder einen value
(wenn erfüllt) oder einen reason
(wenn abgelehnt).
Prinzip: "Alle Ergebnisse abrufen, unabhängig von individuellem Erfolg oder Misserfolg." Dies ist nützlich, wenn Sie das Ergebnis jeder Operation im Batch kennen möchten, auch wenn einige davon fehlschlagen, und Sie mit den erfolgreichen fortfahren können.
Anwendungsfall: Betrachten Sie eine Social-Media-Anwendung, bei der ein Benutzer gleichzeitig mehrere Bilder hochladen möchte. Einige Uploads können erfolgreich sein, andere können aufgrund von Netzwerkproblemen oder ungültigen Dateitypen fehlschlagen. Die Anwendung sollte den Benutzer über den Status jedes Uploads informieren (z. B. "Bild 1 hochgeladen", "Bild 2 fehlgeschlagen"), anstatt den gesamten Vorgang abzubrechen, nur weil ein Bild nicht verarbeitet werden konnte.
Implementierungsbeispiel:
// Simulierte API-Aufrufe const uploadImage = (fileName, artificialFailure = false) => { return new Promise((resolve, reject) => { setTimeout(() => { if (artificialFailure || fileName.includes('failed')) { reject(new Error(`Fehler beim Hochladen von ${fileName}`)); } else { resolve({ fileName, url: `https://example.com/images/${fileName}` }); } }, Math.random() * 1000 + 500); // Simulieren variabler Upload-Zeiten }); }; const imagesToUpload = [ 'foto1.jpg', 'avatar.png', 'korruptes_bild.failed.gif', // Dieses wird fehlschlagen 'dokument.pdf' ]; const uploadPromises = imagesToUpload.map(img => uploadImage(img)); Promise.allSettled(uploadPromises) .then(results => { console.log("Promise.allSettled Ergebnisse:"); const successfulUploads = []; const failedUploads = []; results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`Bild ${imagesToUpload[index]} erfolgreich hochgeladen:`, result.value.url); successfulUploads.push(result.value); } else { console.error(`Bild ${imagesToUpload[index]} konnte nicht hochgeladen werden:`, result.reason.message); failedUploads.push({ fileName: imagesToUpload[index], reason: result.reason.message }); } }); console.log("\nZusammenfassung:"); console.log("Erfolgreich hochgeladen:", successfulUploads); console.log("Fehlgeschlagene Uploads:", failedUploads); // UI basierend auf individuellen Ergebnissen aktualisieren, z. B. Fortschritt für jedes anzeigen }) .catch(error => { // Dieser Catch-Block wird typischerweise von Promise.allSettled nicht erreicht, // es sei denn, das Eingabe-Iterable selbst ist ungültig oder es tritt ein synchroner Fehler auf. console.error("Unerwarteter Fehler bei Promise.allSettled:", error); });
Auswahl zwischen Promise.all und Promise.allSettled
Die Entscheidung hängt weitgehend vom gewünschten Verhalten ab, wenn eine oder mehrere Ihrer API-Anfragen fehlschlagen:
-
Verwenden Sie
Promise.all
, wenn:- Alle Operationen kritisch und voneinander abhängig sind. Wenn eine fehlschlägt, sollte die gesamte Transaktion zurückgerollt oder als unvollständig betrachtet werden.
- Sie nur am kollektiven Erfolg aller Anfragen interessiert sind.
- Ein schnelles Scheitern akzeptabel oder erwünscht ist.
-
Verwenden Sie
Promise.allSettled
, wenn:- Sie Ergebnisse für alle Anfragen verarbeiten müssen, unabhängig von individuellem Erfolg oder Misserfolg.
- Die Operationen weitgehend unabhängig sind und der Ausfall einer nicht den Ausfall des gesamten Batches erfordert.
- Sie dem Benutzer detaillierte Rückmeldungen darüber geben möchten, welche spezifischen Operationen erfolgreich waren und welche fehlgeschlagen sind.
- Sie Fehler elegant behandeln möchten, während Sie dennoch erfolgreiche Ergebnisse verarbeiten.
Fazit
Sowohl Promise.all
als auch Promise.allSettled
sind unschätzbare Werkzeuge für die Steuerung mehrerer asynchroner Operationen, insbesondere im Kontext von Batch-API-Anfragen. Promise.all
erzwingt einen "Alles oder Nichts"-Vertrag, der für voneinander abhängige Aufgaben geeignet ist, bei denen der kollektive Erfolg oberste Priorität hat. Umgekehrt bietet Promise.allSettled
einen robusteren Ansatz, der es Ihnen ermöglicht, den Ausgang jeder Anfrage zu ermitteln, was ihn ideal für Szenarien macht, in denen teilweiser Erfolg akzeptabel ist und die individuelle Statusberichterstattung entscheidend ist. Indem Entwickler ihre unterschiedlichen Verhaltensweisen verstehen und sie entsprechend anwenden, können sie robustere, fehlertolerantere und benutzerfreundlichere JavaScript-Anwendungen erstellen. Wählen Sie Promise.all
für kritische, voneinander abhängige Operationen und Promise.allSettled
für eine robuste Verarbeitung, bei der individuelle Ergebnisse zählen.