Benjamins Antwort bietet eine großartige Abstraktion zur Lösung dieses Problems, aber ich hatte auf eine weniger abstrahierte Lösung gehofft. Die explizite Möglichkeit, dieses Problem zu beheben, besteht darin, einfach .catch
die internen Versprechen aufzurufen und den Fehler von ihrem Rückruf zurückzugeben.
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Wenn Sie noch einen Schritt weiter gehen, können Sie einen generischen Catch-Handler schreiben, der folgendermaßen aussieht:
const catchHandler = error => ({ payload: error, resolved: false });
dann kannst du tun
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
Das Problem dabei ist, dass die abgefangenen Werte eine andere Schnittstelle haben als die nicht abgefangenen Werte. Um dies zu bereinigen, können Sie Folgendes tun:
const successHandler = result => ({ payload: result, resolved: true });
Jetzt können Sie Folgendes tun:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Dann, um es trocken zu halten, kommen Sie zu Benjamins Antwort:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
wo es jetzt aussieht
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Die Vorteile der zweiten Lösung sind, dass sie abstrahiert und trocken ist. Der Nachteil ist, dass Sie mehr Code haben und daran denken müssen, alle Ihre Versprechen zu reflektieren, um die Dinge konsistent zu machen.
Ich würde meine Lösung als explizit und KISS charakterisieren, aber in der Tat weniger robust. Die Benutzeroberfläche garantiert nicht, dass Sie genau wissen, ob das Versprechen erfolgreich war oder fehlgeschlagen ist.
Zum Beispiel könnten Sie dies haben:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
Dies wird nicht durch erwischt werden a.catch
, so
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
Es gibt keine Möglichkeit zu sagen, welches tödlich war und welches nicht. Wenn dies wichtig ist, sollten Sie eine Schnittstelle erzwingen, die nachverfolgt, ob sie erfolgreich war oder nicht (was der reflect
Fall ist).
Wenn Sie Fehler nur ordnungsgemäß behandeln möchten, können Sie Fehler einfach als undefinierte Werte behandeln:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
In meinem Fall muss ich den Fehler nicht kennen oder wissen, wie er fehlgeschlagen ist - es ist mir nur wichtig, ob ich den Wert habe oder nicht. Ich lasse die Funktion, die das Versprechen generiert, sich um die Protokollierung des spezifischen Fehlers kümmern.
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
Auf diese Weise kann der Rest der Anwendung den Fehler ignorieren, wenn er möchte, und ihn als undefinierten Wert behandeln, wenn er möchte.
Ich möchte, dass meine Funktionen auf hoher Ebene sicher ausfallen und mich nicht um die Details kümmern, warum ihre Abhängigkeiten fehlgeschlagen sind, und ich bevorzuge KISS gegenüber DRY, wenn ich diesen Kompromiss eingehen muss - weshalb ich mich letztendlich für die Nichtverwendung entschieden habe reflect
.