0

I use bluebird.js map function to perform parallel requests to an external API I use.
whenever any of the inner promises gets rejected, the map function also gets rejected (I know that this is by design).
I was wondering if there is any way to run promises in parallel but handle each rejection separately, and not fail the entire Promise.

David Tzoor
  • 987
  • 4
  • 16
  • 33
  • 3
    The usual thing would be to convert those rejections into resolutions with a flag value. I don't know bluebird's semantics and syntax, though. With ES2015's promises, it would be just `p = p.catch(function(error) { return /*...something to indicate this resolution is really an error..*/;})` (prior to giving `p` to `map`). – T.J. Crowder Aug 21 '16 at 14:47
  • 1
    @T.J.Crowder it's `.reflect` - added an answer. – Benjamin Gruenbaum Aug 21 '16 at 15:20

3 Answers3

3

I would use .reflect with the map to get promise inspections as the result:

const all = Promise.map(items, item => doSomething(item).reflect());

all.then(items => {
  // will contain an array of the promises that fulfilled correctly
  let fulfilled = items.filter(x => x.isFulfilled()).map(x => x.value()));
  // will contain an array of the promises that rejected 
  // so you can handle the errors
  let rejected = items.filter(x => x.isRejected()).map(x => x.reason());

});
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
2

I was wondering if there is any way to run promises in parallel but handle each rejection separately, and not fail the entire Promise.

Sure there is - just handle them using catch or error:

const all = Promise.map(somethings, function(something) {
    return doSomething(something).catch(function(e) {
        return handleIt(e); // normal completion, no rejection or exception
    });
});

See also Wait until all ES6 promises complete, even rejected promises in general.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • `Promise.map` is not a standard api. Please add which npm package you are using for Promises. – zoran404 Dec 14 '18 at 18:44
  • @zoran404 The question is tagged [tag:Bluebird] so that's what I was using. The answers in the linked question work with native promises though – Bergi Dec 14 '18 at 18:51
  • Ok and sorry, I was looking for answers and didn't read the question after the title :) – zoran404 Dec 14 '18 at 19:12
-1

Without any library with pure ES6 promises you may have an approach like this. In this case the promiseAll function would honor resolved promises along with the rejected ones.

function promisify(fun){
  return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}

function async(data, callback){
  data.val+= " msec";
  Math.random() < 0.5 ? setTimeout(_ => callback(false,data.val),data.dur)
                      : setTimeout(_ => callback("error"),data.dur);
}

function myNormalCallback(resultObject){
  console.log("Promise " + resultObject.count + " returned " + resultObject.result);
}

function myErrorCallback(errorObject){
  console.log("Promise " + errorObject.count + " returned " + errorObject.error);
}

function promiseAll(proms){
  return new Promise((v,x) => { var results = new Array(proms.length).fill(void 0);
                                proms = proms.map((prom,i) => prom.then(res  => {results[i] = res;
                                                                                 results.indexOf(void 0) === -1 && v(results);
                                                                                })
                                                                  .catch(err => {results[i] = err;
                                                                                 results.indexOf(void 0) === -1 && v(results);
                                                                                }));
                              });
}

var datas = [{val: 100, dur: 1000},{val: 200, dur: 2000},{val: 300, dur: 3000}],
    proms = datas.map(data => promisify(async)(data));
promiseAll(proms).then(results => results.map((res,i) => res === "error" ? myErrorCallback({count:i,error: res})
                                                                         : myNormalCallback({count:i,result: res})));
Redu
  • 25,060
  • 6
  • 56
  • 76