Lets say I have a set of promises that are making network requests, of which one will fail:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Lets say I want to wait until all of these have finished, regardless of if one has failed. There might be a network error for a resource that I can live without, but which if I can get, I want before I proceed. I want to handle network failures gracefully.
Since Promises.all
doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?
I think the following offers a slightly different approach... compare
fn_fast_fail()
withfn_slow_fail()
... though the latter doesn't fail as such... you can check if one or both ofa
andb
is an instance ofError
andthrow
thatError
if you want it to reach thecatch
block (e.g.if (b instanceof Error) { throw b; }
) . See the jsfiddle.I really like Benjamin's answer, and how he basically turns all promises into always-resolving-but-sometimes-with-error-as-a-result ones. :)
Here's my attempt at your request just in case you were looking for alternatives. This method simply treats errors as valid results, and is coded similar to
Promise.all
otherwise:I had the same problem and have solved it in the following way:
In that case
Promise.all
will wait for every Promise will come intoresolved
orrejected
state.And having this solution we are "stopping
catch
execution" in a non-blocking way. In fact, we're not stopping anything, we just returning back thePromise
in a pending state which returns anotherPromise
when it's resolved after the timeout.Benjamin's answer offers a great abstraction for solving this issue, but I was hoping for a less abstracted solution. The explicit way to to resolve this issue is to simply call
.catch
on the internal promises, and return the error from their callback.Taking this one step further, you could write a generic catch handler that looks like this:
then you can do
The problem with this is that the caught values will have a different interface than the non-caught values, so to clean this up you might do something like:
So now you can do this:
Then to keep it DRY, you get to Benjamin's answer:
where it now looks like
The benefits of the second solution are that its abstracted and DRY. The downside is you have more code, and you have to remember to reflect all your promises to make things consistent.
I would characterize my solution as explicit and KISS, but indeed less robust. The interface doesn't guarantee that you know exactly whether the promise succeeded or failed.
For example you might have this:
This won't get caught by
a.catch
, soThere's no way to tell which one was fatal and which was wasn't. If that's important then you're going to want to enforce and interface that tracks whether it was successful or not (which
reflect
does).If you just want to handle errors gracefully, then you can just treat errors as undefined values:
In my case, I don't need to know the error or how it failed--I just care whether I have the value or not. I'll let the function that generates the promise worry about logging the specific error.
That way, the rest of the application can ignore its error if it wants, and treat it as an undefined value if it wants.
I want my high level functions to fail safely and not worry about the details on why its dependencies failed, and I also prefer KISS to DRY when I have to make that tradeoff--which is ultimately why I opted to not use
reflect
.I don't know which promise library you are using, but most have something like allSettled.
Edit: Ok since you want to use plain ES6 without external libraries, there is no such method.
In other words: You have to loop over your promises manually and resolve a new combined promise as soon as all promises are settled.
This should be consistent with how Q does it: