572

Let's 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   

Let's 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 Promise.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?

gligoran
  • 3,267
  • 3
  • 32
  • 47
Nathan Hagen
  • 12,440
  • 4
  • 26
  • 31
  • What should be returned in resulting array for promises that rejected? – Kuba Wyrostek Jul 15 '15 at 08:07
  • 10
    ES6 promises support no such method (and are currently apparently [slower than Bluebird](http://programmers.stackexchange.com/a/279003/110316)). In addition, not all browsers or engines support them just yet. I would *strongly* recommend using Bluebird, which comes with `allSettled` which satisfies your need without you having to roll your own. – Dan Jul 15 '15 at 08:10
  • @KubaWyrostek I think you bring up the reason Promise.all doesn't have this behavior, which I think makes sense. This isn't how it works, but an alternative view would be to say Promise.all should return a special promise that never fails--and you would get the error that was thrown as the argument representing the failed promise. – Nathan Hagen Jul 15 '15 at 19:05
  • 1
    To add to what Dan shared, the allSettled / settleAll like functionality that bluebird has can be utilized via the "reflect" function. – user3344977 Sep 19 '16 at 23:10
  • @NathanHagen For understanding: Today I could just do `Promise.all(arr).finally(...)`? – Coli Jun 13 '18 at 17:52
  • 2
    @Coli: Hmm, I don't think so. `Promise.all` will reject as soon as *any one* promise rejects, so your proposed idiom does not guarantee that all promises are settled. – Jörg W Mittag Oct 28 '18 at 21:49
  • I think it's worth sharing the solution provided here - very clean with async/await - https://stackoverflow.com/a/46024590/4735481 – does_not_compute Jan 28 '19 at 10:59
  • circa 2019 - without a promise library? DRY always consult sindresorhous for one off promise solutions. https://npms.io/search?q=author%3Asindresorhus+promise, In this case p-settle. https://npms.io/search?q=author%3Asindresorhus+p-settle – DKebler Mar 05 '19 at 17:53
  • I would recommend you using my basic library https://www.npmjs.com/package/promise-all-always it does exactly what you need. Check examples. – Osoian Marcel Apr 09 '19 at 09:15

20 Answers20

468

Update, you probably want to use the built-in native Promise.allSettled:

Promise.allSettled([promise]).then(([result]) => {
   //reach here regardless
   // {status: "fulfilled", value: 33}
});

As a fun fact, this answer below was prior art in adding that method to the language :]


Sure, you just need a reflect:

const reflect = p => p.then(v => ({v, status: "fulfilled" }),
                            e => ({e, status: "rejected" }));

reflect(promise).then((v) => {
    console.log(v.status);
});

Or with ES5:

function reflect(promise){
    return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                        function(e){ return {e:e, status: "rejected" }});
}


reflect(promise).then(function(v){
    console.log(v.status);
});

Or in your example:

var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr.map(reflect)).then(function(results){
    var success = results.filter(x => x.status === "fulfilled");
});
Matan Bobi
  • 2,693
  • 1
  • 15
  • 27
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 3
    I think this is a great solution. Can you amend it to include a simpler syntax? The crux of the issue is that if you want to handle errors in sub-promises, you should catch them and return the error. So for example: https://gist.github.com/nhagen/a1d36b39977822c224b8 – Nathan Hagen Jul 15 '15 at 17:20
  • 1
    Simpler syntax? I don't understand what you mean. – Benjamin Gruenbaum Jul 15 '15 at 17:32
  • You solve the problem the best way, but you do much more on top of it. The main problem with the example in the original question is that I should have called `.catch()`, or passed in a second function to `.then()`, on the individual promises (and returned their errors). I think adding the `status` property and then filtering obfuscates a more general solution which is what I was looking for. – Nathan Hagen Jul 15 '15 at 19:11
  • 4
    @NathanHagen it lets you figure out what rejected and what fulfilled and extracts the problem to a reusable operator. – Benjamin Gruenbaum Jul 15 '15 at 19:14
  • I agree, I just think the answer could be more explicit about the underlying, less abstracted solution. Just a suggestion; I don't mean to be pedantic. I did ask for what the best pattern is, so this obviously a valid and complete answer. – Nathan Hagen Jul 15 '15 at 19:21
  • Does anyone know of an npm package that implements a function like this for native promises? I am going to need to implement this across multiple services so it seems like something worth abstracting to a dependency rather than duplicating. – SamF Oct 10 '16 at 14:15
  • 5
    In response to my own issue I have created the following npm package: https://github.com/Bucabug/promise-reflect https://www.npmjs.com/package/promise-reflect – SamF Oct 11 '16 at 07:44
  • 2
    I ran into this issue a while ago and I created this npm package for it: https://www.npmjs.com/package/promise-all-soft-fail – velocity_distance Nov 28 '16 at 22:40
  • 5
    Is the word `reflect` a common word in computer science? Can you please link to where this is explained like on wikipedia or something. I was searching hard for `Promise.all not even first reject` but didn't know to search "Reflect". Should ES6 have a `Promise.reflect` which is like "Promise.all but really all"? – Noitidart Sep 19 '18 at 01:00
  • 1
    We pretty much invented most of this terminology as we went specifying the APIs - in this case for bluebird. I am not aware of much broad usage (reflection is used for inspecting state in programming). I am not too attached to the term :) – Benjamin Gruenbaum Sep 21 '18 at 17:56
  • 1
    Personally I think it's a huge shame that the builtin `Promise.all` doesn't provide an option to wait for all promises to finish... – Andy Feb 14 '19 at 20:10
  • 1
    It's worth noting that the ES2020 `Promise.allSettled` can accept a heterogeneous array. Elements that are not a `Promise` are implicitly resolved. Thus I've modified my version of `reflect` to allow this: `const reflect = p => (p instanceof Promise) ? p.then(v => ({ value: v, status: "fulfilled" }), e => ({ reason: e, status: "rejected" })) : { status: 'fulfilled', value: p };` – David G Sep 01 '20 at 23:51
  • Unfortunately Promise.allSettled doesn't seem to provide an easy way to get the actual results like Promise.all returns. This was needed for my use case with react. – wheredidthatnamecomefrom May 29 '22 at 16:34
285

Similar answer, but more idiomatic for ES6 perhaps:

const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);

Promise.all([a, b, c].map(p => p.catch(e => e)))
  .then(results => console.log(results)) // 1,Error: 2,3
  .catch(e => console.log(e));


const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>

Depending on the type(s) of values returned, errors can often be distinguished easily enough (e.g. use undefined for "don't care", typeof for plain non-object values, result.message, result.toString().startsWith("Error:") etc.)

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
jib
  • 40,579
  • 17
  • 100
  • 158
  • This is a great solution, but I am wondering how if you had the following `var a = () => setTimeout(() => Promise.resolve(1), 2000)` then `b()` would be rejected before `a()`? I would really like to know how to wait for all promises to resolve, before proceeding. Please view http://stackoverflow.com/questions/41265291/waiting-for-promises-to-resolve-within-an-array if you have an answer ;-) – stackunderflow Dec 21 '16 at 15:00
  • 2
    @KarlBateman I think you're confused. The order functions resolve or reject in don't matter here since the `.map(p => p.catch(e => e))` part turns all rejections into resolved values, so `Promise.all` still waits for everything to finish whether individual functions resolve or reject, regardless of how long they take. Try it. – jib Dec 21 '16 at 15:44
  • Forgive my lack of understanding there, yes your solution does indeed work the way I needed. Thank you ;-) – stackunderflow Dec 21 '16 at 16:37
  • 44
    `.catch(e => console.log(e));` is never called because this never fails – fregante Mar 02 '17 at 11:31
  • 5
    @bfred.it That's correct. Though terminating promise chains with `catch` is generally good practice [IMHO](http://stackoverflow.com/questions/33376308/es6-promises-swallow-type-errors/33382482#33382482). – jib Mar 02 '17 at 15:11
  • Could you please explain what does `p => p.catch(e => e)` do? What does `e=>e` mean? I understand the arrow functions, I do not understand the alone `e` in `=> e` – Suhail Gupta Mar 21 '17 at 04:10
  • 3
    @SuhailGupta It catches the error `e` and returns it as a regular (success) value. Same as `p.catch(function(e) { return e; })` only shorter. `return` is implicit. – jib Mar 21 '17 at 04:54
  • its called if any error happens in the `then` block @bfred.it – Bowofola Jun 16 '17 at 19:29
  • Since all promises in Promise.all are guaranteed to resolve, doesn't this mean that Promise.all is also guaranteed to resolve and therefore the .catch() on Promise.all is unreachable code? I am trying to figure out what to put in this section but can't think of a situation in which that catch block would ever be reached. – Justin Reusnow Nov 09 '17 at 17:00
  • 1
    @JustinReusnow already covered in comments. Always good practice to terminate chains in case you add code later. – jib Nov 09 '17 at 19:06
  • @JustinReusnow In his example it doesn't matter. It's useless. But if the then() callback return a promise. Then the chain can make sense. Like when you have a promise that if it resolve, then nothing to do, and everything ok. But if it reject, then we need to handle it. – Mohamed Allal Feb 04 '19 at 15:47
  • doc : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all#Promise.all_fail-fast_behaviour – Mohamed Allal Feb 04 '19 at 16:00
  • 1
    @jib I think in this case there's a significant risk of confusing developers who are less familiar with how promises work into thinking that `p.catch(e => e)` still rejects because it returns an `Error`, even though in reality it resolves to the `Error`. – Andy Feb 14 '19 at 20:13
  • @Andy I don't think it requires familiarity with promises to know a return from a `catch` doesn't re-propagate the error, since the same is true in synchronous `try`/`catch` code. It's different from rethrow: `p.catch(e => Promise.reject(e))`, i.e. return "wait for doomed future-work". – jib Feb 14 '19 at 22:22
  • True...could just use `p.catch(console.error)` and `p.catch(Promise.reject)` instead of the lambda functions – Andy Feb 16 '19 at 00:42
87

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.

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));

Taking this one step further, you could write a generic catch handler that looks like this:

const catchHandler = error => ({ payload: error, resolved: false });

then you can do

> 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 } ]

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:

const successHandler = result => ({ payload: result, resolved: true });

So now you can do this:

> 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!' ]

Then to keep it DRY, you get to Benjamin's answer:

const reflect = promise => promise
  .then(successHandler)
  .catch(catchHander)

where it now looks like

> 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!' ]

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:

const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));

This won't get caught by a.catch, so

> Promise.all([a, b].map(promise => promise.catch(e => e))
    .then(results => console.log(results))
< [ Error, Error ]

There'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:

> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
    .then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]

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.

const apiMethod = () => fetch()
  .catch(error => {
    console.log(error.message);
    throw 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.

Shmidt
  • 16,436
  • 18
  • 88
  • 136
Nathan Hagen
  • 12,440
  • 4
  • 26
  • 31
  • 1
    @Benjamin I think @Nathan's solution is very straightforward and idiomatic for `Promise`s. While your `reflect` improves code reuse, it also establishes another level of abstraction. Since Nathan's answer has so far only received a fraction of upvotes compared to yours, I wonder whether this is an indication of an issue with his solution, which I haven't recognized yet. –  Jul 09 '16 at 10:54
  • 2
    @LUH3417 this solution is conceptually less sound since it treats errors as values and does not separate errors from non-errors. For example if one of the promises resolves legitimately to a value that can be thrown (which is entirely possible) this breaks quite badly. – Benjamin Gruenbaum Aug 02 '16 at 14:25
  • 2
    @BenjaminGruenbaum So for example, `new Promise((res, rej) => res(new Error('Legitimate error'))` would not be distinguishable from `new Promise(((res, rej) => rej(new Error('Illegitimate error'))`? Or further, you wouldn't be able to filter by `x.status`? I'll add this point to my answer so the difference is more clear – Nathan Hagen Aug 02 '16 at 15:53
  • 3
    The reason this is a poor idea is because it ties the Promise implementation to a specific use case of only ever being used in a specific ```Promise.all()``` variant, it also then becomes incumbent on the Promise consumer to know that a specific promise won't reject but will swallow it's errors. In fact the ```reflect()``` method could be made less 'abstract' and more explicit by calling it ```PromiseEvery(promises).then(...)```.The complexity of the answer above compared to Benjamin's should say much about this solution. – Neil Sep 28 '16 at 21:51
45

There is a finished proposal for a function which can accomplish this natively, in vanilla Javascript: Promise.allSettled, which has made it to stage 4, is officialized in ES2020, and is implemented in all modern environments. It is very similar to the reflect function in this other answer. Here's an example, from the proposal page. Before, you would have had to do:

function reflect(promise) {
  return promise.then(
    (v) => {
      return { status: 'fulfilled', value: v };
    },
    (error) => {
      return { status: 'rejected', reason: error };
    }
  );
}

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');

Using Promise.allSettled instead, the above will be equivalent to:

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');

Those using modern environments will be able to use this method without any libraries. In those, the following snippet should run without problems:

Promise.allSettled([
  Promise.resolve('a'),
  Promise.reject('b')
])
  .then(console.log);

Output:

[
  {
    "status": "fulfilled",
    "value": "a"
  },
  {
    "status": "rejected",
    "reason": "b"
  }
]

For older browsers, there is a spec-compliant polyfill here.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
10

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:

Promise.settle = function(promises) {
  var results = [];
  var done = promises.length;

  return new Promise(function(resolve) {
    function tryResolve(i, v) {
      results[i] = v;
      done = done - 1;
      if (done == 0)
        resolve(results);
    }

    for (var i=0; i<promises.length; i++)
      promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
    if (done == 0)
      resolve(results);
  });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Kuba Wyrostek
  • 6,163
  • 1
  • 22
  • 40
  • This is typically called `settle`. We have that too in bluebird, I like reflect better but this is a viable solution for when you have this for an array. – Benjamin Gruenbaum Jul 15 '15 at 08:31
  • 2
    OK, *settle* will be a better name indeed. :) – Kuba Wyrostek Jul 15 '15 at 08:34
  • This looks a lot like the explicit promise construction antipattern. It should be noted that you never should write such a function yourself, but use the one your library supplies (OK, native ES6 is a bit meager). – Bergi Jul 16 '15 at 19:25
  • Could you please use the `Promise` constructor properly (and avoid that `var resolve` thingy)? – Bergi Jul 16 '15 at 19:26
  • Bergi, feel free to alter the answer however you consider necessary. – Kuba Wyrostek Jul 16 '15 at 19:33
  • This looks a lot, but probably isn't. There is no single *inner* promise that can be passed up instead of creating a new one. But taking into consideration you being an expert in the case I promise to really think it over. :) As for `resolve` thingy it's just I miss defer syntax *much*. – Kuba Wyrostek Jul 16 '15 at 19:51
  • OK, I've removed the `resolve` variable. Would the person who downvoted be kind enough to explain? – Kuba Wyrostek Jul 17 '15 at 07:32
  • Thanks a lot, that's what I was looking for :-) The `Promise` constructor has the unbeatable advantage over deferreds that it catches exceptions (e.g. when `promises` is not what we expect it to be). – Bergi Jul 17 '15 at 13:03
  • That's true, but I can think of cases when promise is created in one place and resolved/rejected in another place in code. So deferreds (or keeping reference to resolve/reject) are necessary then, aren't they? – Kuba Wyrostek Jul 17 '15 at 13:24
  • It is a clever way to pass promise result to the main promise. Nice. – Hossein Nasr Nov 24 '18 at 19:42
5
var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

The Promise.all will swallow any rejected promise and store the error in a variable, so it will return when all of the promises have resolved. Then you can re-throw the error out, or do whatever. In this way, I guess you would get out the last rejection instead of the first one.

martin770
  • 1,271
  • 11
  • 15
  • 1
    Seems like this could aggregate errs by making it an array and using `err.push(error)`, so all errors could be bubbled up. – ps2goat Apr 06 '18 at 15:03
4

I had the same problem and have solved it in the following way:

const fetch = (url) => {
  return node-fetch(url)
    .then(result => result.json())
    .catch((e) => {
      return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
    });
};

tasks = [fetch(url1), fetch(url2) ....];

Promise.all(tasks).then(......)

In that case Promise.all will wait for every Promise will come into resolved or rejected 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 the Promise in a pending state which returns another Promise when it's resolved after the timeout.

user1016265
  • 2,307
  • 3
  • 32
  • 49
  • But that invokes all promises at will when you run `Promise.all`. I'm looking for a way to listen for when all promises have been invoked, but not invoke them myself. Thanks. – SudoPlz Jan 03 '18 at 12:25
  • @SudoPlz the method `all()` does that, it waits for the fulfilment of all Promises or rejection at least one of it. – user1016265 Jan 03 '18 at 20:46
  • that's true, but it doesn't just wait, it actually invokes/starts/fires up the process. If you wished to fire up the promises someplace else that wouldn't be possible, becase `.all` fires everything up. – SudoPlz Jan 03 '18 at 21:10
  • @SudoPlz hope this will change your opinion https://jsfiddle.net/d1z1vey5/ – user1016265 Jan 04 '18 at 21:35
  • 3
    I stand corrected. Until now I thought Promises only run when someone invokes them (a.k.a a `then` or an `.all` call) but they run when created. – SudoPlz Jan 05 '18 at 00:23
3

This should be consistent with how Q does it:

if(!Promise.allSettled) {
    Promise.allSettled = function (promises) {
        return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
            state: 'fulfilled',
            value: v,
        }), r => ({
            state: 'rejected',
            reason: r,
        }))));
    };
}
mpen
  • 272,448
  • 266
  • 850
  • 1,236
3

Instead of rejecting, resolve it with a object. You could do something like this when you are implementing promise

const promise = arg => {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
        try{
          if(arg != 2)
            return resolve({success: true, data: arg});
          else
            throw new Error(arg)
        }catch(e){
          return resolve({success: false, error: e, data: arg})
        }
      }, 1000);
  })
}

Promise.all([1,2,3,4,5].map(e => promise(e))).then(d => console.log(d))
noone
  • 6,168
  • 2
  • 42
  • 51
2

Benjamin Gruenbaum answer is of course great,. But I can also see were Nathan Hagen point of view with the level of abstraction seem vague. Having short object properties like e & v don't help either, but of course that could be changed.

In Javascript there is standard Error object, called Error,. Ideally you always throw an instance / descendant of this. The advantage is that you can do instanceof Error, and you know something is an error.

So using this idea, here is my take on the problem.

Basically catch the error, if the error is not of type Error, wrap the error inside an Error object. The resulting array will have either resolved values, or Error objects you can check on.

The instanceof inside the catch, is in case you use some external library that maybe did reject("error"), instead of reject(new Error("error")).

Of course you could have promises were you resolve an error, but in that case it would most likely make sense to treat as an error anyway, like the last example shows.

Another advantage of doing it this, array destructing is kept simple.

const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);

Instead of

const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }

You could argue that the !error1 check is simpler than an instanceof, but your also having to destruct both v & e.

function PromiseAllCatch(promises) {
  return Promise.all(promises.map(async m => {
    try {
      return await m;
    } catch(e) {
      if (e instanceof Error) return e;
      return new Error(e);
    }
  }));
}


async function test() {
  const ret = await PromiseAllCatch([
    (async () => "this is fine")(),
    (async () => {throw new Error("oops")})(),
    (async () => "this is ok")(),
    (async () => {throw "Still an error";})(),
    (async () => new Error("resolved Error"))(),
  ]);
  console.log(ret);
  console.log(ret.map(r =>
    r instanceof Error ? "error" : "ok"
    ).join(" : ")); 
}

test();
Keith
  • 22,005
  • 2
  • 27
  • 44
1

I think the following offers a slightly different approach... compare fn_fast_fail() with fn_slow_fail()... though the latter doesn't fail as such... you can check if one or both of a and b is an instance of Error and throw that Error if you want it to reach the catch block (e.g. if (b instanceof Error) { throw b; }) . See the jsfiddle.

var p1 = new Promise((resolve, reject) => { 
    setTimeout(() => resolve('p1_delayed_resolvement'), 2000); 
}); 

var p2 = new Promise((resolve, reject) => {
    reject(new Error('p2_immediate_rejection'));
});

var fn_fast_fail = async function () {
    try {
        var [a, b] = await Promise.all([p1, p2]);
        console.log(a); // "p1_delayed_resolvement"
        console.log(b); // "Error: p2_immediate_rejection"
    } catch (err) {
        console.log('ERROR:', err);
    }
}

var fn_slow_fail = async function () {
    try {
        var [a, b] = await Promise.all([
            p1.catch(error => { return error }),
            p2.catch(error => { return error })
        ]);
        console.log(a); // "p1_delayed_resolvement"
        console.log(b); // "Error: p2_immediate_rejection"
    } catch (err) {
        // we don't reach here unless you throw the error from the `try` block
        console.log('ERROR:', err);
    }
}

fn_fast_fail(); // fails immediately
fn_slow_fail(); // waits for delayed promise to resolve
drmrbrewer
  • 11,491
  • 21
  • 85
  • 181
1

I just wanted a polyfill that exactly replicated ES2020 behaviour since I'm locked into node versions a lot earlier than 12.9 (when Promise.allSettled appeared), unfortunately. So for what it's worth, this is my version:

const settle = (promise) => (promise instanceof Promise) ?
  promise.then(val => ({ value: val, status: "fulfilled" }),
               err => ({ reason: err, status: "rejected" })) :
  { value: promise, status: 'fulfilled' };

const allSettled = async (parr) => Promise.all(parr.map(settle));

This handles a mixed array of promise and non-promise values, as does the ES version. It hands back the same array of { status, value/reason } objects as the native version.

David G
  • 5,408
  • 1
  • 23
  • 19
0

Here's my custom settledPromiseAll()

const settledPromiseAll = function(promisesArray) {
  var savedError;

  const saveFirstError = function(error) {
    if (!savedError) savedError = error;
  };
  const handleErrors = function(value) {
    return Promise.resolve(value).catch(saveFirstError);
  };
  const allSettled = Promise.all(promisesArray.map(handleErrors));

  return allSettled.then(function(resolvedPromises) {
    if (savedError) throw savedError;
    return resolvedPromises;
  });
};

Compared to Promise.all

  • If all promises are resolved, it performs exactly as the standard one.

  • If one of more promises are rejected, it returns the first one rejected much the same as the standard one but unlike it waits for all promises to resolve/reject.

For the brave we could change Promise.all():

(function() {
  var stdAll = Promise.all;

  Promise.all = function(values, wait) {
    if(!wait)
      return stdAll.call(Promise, values);

    return settledPromiseAll(values);
  }
})();

CAREFUL. In general we never change built-ins, as it might break other unrelated JS libraries or clash with future changes to JS standards.

My settledPromiseall is backward compatible with Promise.all and extends its functionality.

People who are developing standards -- why not include this to a new Promise standard?

Simon B.
  • 2,530
  • 24
  • 30
Edward
  • 91
  • 1
  • 5
0

I recently built a library that allows what you need. it executes promises in parallel, and if one fails, the process continues, at the end it returns an array with all the results, including errors.

https://www.npmjs.com/package/promise-ax

I hope and it is helpful for someone.

const { createPromise } = require('promise-ax');
const promiseAx = createPromise();
const promise1 = Promise.resolve(4);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, new Error("error")));
const promise3 = Promise.reject("error");
const promise4 = promiseAx.resolve(8);
const promise5 = promiseAx.reject("errorAx");
const asyncOperation = (time) => {
    return new Promise((resolve, reject) => {
        if (time < 0) {
            reject("reject");
        }
        setTimeout(() => {
            resolve(time);
        }, time);
    });
};
const promisesToMake = [promise1, promise2, promise3, promise4, promise5, asyncOperation(100)];
promiseAx.allSettled(promisesToMake).then((results) =>   results.forEach((result) => console.log(result)));
// Salida esperada:
// 4
// Error: error
// error
// 8
// errorAx
// 100
-1

I would do:

var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];

Promise.all(err)
.then(function (res) { console.log('success', res) })
.catch(function (err) { console.log('error', err) }) //never executed
FRocha
  • 942
  • 7
  • 11
-1

I've been using following codes since ES5.

Promise.wait = function(promiseQueue){
    if( !Array.isArray(promiseQueue) ){
        return Promise.reject('Given parameter is not an array!');
    }

    if( promiseQueue.length === 0 ){
        return Promise.resolve([]);
    }

    return new Promise((resolve, reject) =>{
        let _pQueue=[], _rQueue=[], _readyCount=false;
        promiseQueue.forEach((_promise, idx) =>{
            // Create a status info object
            _rQueue.push({rejected:false, seq:idx, result:null});
            _pQueue.push(Promise.resolve(_promise));
        });

        _pQueue.forEach((_promise, idx)=>{
            let item = _rQueue[idx];
            _promise.then(
                (result)=>{
                    item.resolved = true;
                    item.result = result;
                },
                (error)=>{
                    item.resolved = false;
                    item.result = error;
                }
            ).then(()=>{
                _readyCount++;

                if ( _rQueue.length === _readyCount ) {
                    let result = true;
                    _rQueue.forEach((item)=>{result=result&&item.resolved;});
                    (result?resolve:reject)(_rQueue);
                }
            });
        });
    });
};

The usage signature is just like Promise.all. The major difference is that Promise.wait will wait for all the promises to finish their jobs.

-1

I know that this question has a lot of answers, and I'm sure must (if not all) are correct. However it was very hard for me to understand the logic/flow of these answers.

So I looked at the Original Implementation on Promise.all(), and I tried to imitate that logic - with the exception of not stopping the execution if one Promise failed.

  public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
  {
    let promise: Promise<{ data: any, isSuccess: boolean }[]>;

    if (promisesList.length)
    {
      const result: { data: any, isSuccess: boolean }[] = [];
      let count: number = 0;

      promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
      {
        promisesList.forEach((currentPromise: Promise<any>, index: number) =>
        {
          currentPromise.then(
            (data) => // Success
            {
              result[index] = { data, isSuccess: true };
              if (promisesList.length <= ++count) { resolve(result); }
            },
            (data) => // Error
            {
              result[index] = { data, isSuccess: false };
              if (promisesList.length <= ++count) { resolve(result); }
            });
        });
      });
    }
    else
    {
      promise = Promise.resolve([]);
    }

    return promise;
  }

Explanation:
- Loop over the input promisesList and execute each Promise.
- No matter if the Promise resolved or rejected: save the Promise's result in a result array according to the index. Save also the resolve/reject status (isSuccess).
- Once all Promises completed, return one Promise with the result of all others.

Example of use:

const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);

promiseExecuteAll([p1, p2, p3]).then((data) => {
  data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});

/* Output: 
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/
Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
  • 2
    Don't try to re-implement `Promise.all` yourself, there are too many things that will go wrong. Your version doesn't handle empty inputs for example. – Bergi Mar 08 '19 at 14:04
-2

You can execute your logic sequentially via synchronous executor nsynjs. It will pause on each promise, wait for resolution/rejection, and either assign resolve's result to data property, or throw an exception (for handling that you will need try/catch block). Here is an example:

function synchronousCode() {
    function myFetch(url) {
        try {
            return window.fetch(url).data;
        }
        catch (e) {
            return {status: 'failed:'+e};
        };
    };
    var arr=[
        myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
        myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
        myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
    ];
    
    console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};

nsynjs.run(synchronousCode,{},function(){
    console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
amaksr
  • 7,555
  • 2
  • 16
  • 17
-3

Promise.all with using modern async/await approach

const promise1 = //...
const promise2 = //...

const data = await Promise.all([promise1, promise2])

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]
Maksim Shamihulau
  • 1,219
  • 1
  • 15
  • 17
  • Unfortunately the `Promise#all` method differs slightly from what the question author is looking for. From MDN, "It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error." – Trent Jul 20 '20 at 22:49
-5

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.

Sebastian S
  • 4,420
  • 4
  • 34
  • 63
  • I've edited my question to clarify--Since ES6 comes with promises, I'd like to avoid using another library for what I would think is basic functionality. I guess a good place to get the answer from would be to copy the source from one of the promise libraries. – Nathan Hagen Jul 15 '15 at 08:05