2

I'm looking for the way to get both resolutions and rejections from promise array. I'm currently counting on Bluebird implementation, so ES6 compatible solution would be suitable, too.

The best thing that comes to mind is to use Bluebird's Promise.settle for that, and I consider promise inspections an unnecessary complication here:

  let promises = [
    Promise.resolve('resolved'),
    Promise.resolve('resolved'),
    Promise.reject('rejected')
  ];

  // is there an existing way to do this?
  let resolvedAndRejected = Promise.settle(promises)
  .then((inspections) => {
    let resolved = [];
    let rejected = [];

    inspections.forEach((inspection) => {
      if (inspection.isFulfilled())
        resolved.push(inspection.value());
      else if (inspection.isRejected())
        rejected.push(inspection.reason());
    });

    return [resolved, rejected];
  });

  resolvedAndRejected.spread((resolved, rejected) => {
    console.log(...resolved);
    console.error(...rejected);
  });

It looks like a trivial task for the cases where 100% fulfillment rate isn't an option or a goal, but I don't even know the name for the recipe.

Is there a neat and well-proven way to handle this in Bluebird or other promise implementations - built-in operator or extension?

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • You can use `Promise.all()`, handle rejected promises, return `reason` to chained `.then()`, see http://stackoverflow.com/questions/35042068/why-is-onrejected-not-called-following-promise-all-where-promise-reject-incl; see also http://stackoverflow.com/questions/28131082/jquery-ajax-prevent-fail-in-a-deferred-sequential-loop/ – guest271314 Jun 19 '16 at 01:10
  • @guest271314 I see difficulties with distinguishing caught rejections from resolutions in this case. I believe that the first question considers some different scenario. Your answer on `$.when.all` looks very similar, but I cannot imagine how $ deferreds could be translated to A+ promises in this case - deferreds expose their state, while promises don't. – Estus Flask Jun 19 '16 at 02:50
  • The approach is to handle rejected promise. Once a rejected promise is handled, the `reason` or value can be returned to chained `.then()`, where the value would then be a resolved promise, then store both resolved, rejected promises to an array or other object. Note the `.then()` within `.map()` at first link, where rejected promise is handled. See also http://stackoverflow.com/questions/33630870/conditional-on-promise-all/, and this poorly cobbled together Question http://stackoverflow.com/questions/33688342/how-to-return-accumulated-returned-promise-values-as-array-to-then-following – guest271314 Jun 19 '16 at 02:57
  • What are you going to with the list of resolved and rejected promises once you get it? –  Jun 19 '16 at 04:52
  • @guest271314 Thanks, I think I've got the idea. Feel free to submit the answer if you will come up with something. – Estus Flask Jun 19 '16 at 05:13
  • @torazaburo I've stumbled upon the same pattern several times. My current case is http request queue, depending on the settings and success/fail ratio rejected requests may be re-added to the queue or ignored. – Estus Flask Jun 19 '16 at 05:19

3 Answers3

4

Added answer for completeness since OP asked. Here is what I'd do:

 const res = Promise.all(promises.map(p => p.reflect())) // get promises
   .then(values => [
          values.filter(x => x.isFulfilled()).map(x => x.value()), // resolved
          values.filter(x => x.isRejected()).map(x => x.reason()) // rejected
   ]);
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • This may be `Promise.all(promises.map(p => Promise.resolve(p).reflect()))...` to support non-Bluebird promises, I guess. – Estus Flask Jun 23 '16 at 02:12
3

There's nothing built in for it, but reduce can make it pretty succinct:

Promise
  .settle(promises)
  .reduce(([resolved, rejected], inspection) => {
    if (inspection.isFulfilled())
      resolved.push(inspection.value());
    else
      rejected.push(inspection.reason());
    return [resolved, rejected];
  }, [[], []]);
Jacob
  • 77,566
  • 24
  • 149
  • 228
  • Very neat indeed, thanks! I believe `isRejected()` is there [for a *reason*](http://bluebirdjs.com/docs/api/reason.html). Not sure if settled promise can become 'pending', but it can certainly be 'cancelled'. – Estus Flask Jun 19 '16 at 02:28
  • 1
    I thought `settle` was deprecated. –  Jun 19 '16 at 04:52
  • 3
    Seems to be; can replace that part with `Promise.all(promises.map(p => p.reflect()))` – Jacob Jun 19 '16 at 05:33
  • Settle is deprecated and your change suggestion is good, please implemented :) – Benjamin Gruenbaum Jun 19 '16 at 14:25
  • 1
    `promises.map(p => p.reflect()).all().then(values => [values.filter(x => x.isFulfilled()).map(x => x.value()), values.filter(x => x.isRejected()).map(x => x.reason())])` basically. – Benjamin Gruenbaum Jun 19 '16 at 14:27
  • FWIW, I use `bluebird-settle` to forwardport a deprecated method. Nice one, @BenjaminGruenbaum! You could submit this as an answer for visibility. There's a typo, I guess this should be `Promise.all(promises.map(p => p.reflect()))`. – Estus Flask Jun 19 '16 at 14:52
  • @estus I deprecated settle, so if you're going to port it since you find it useful - I want to know why. I'll add an answer. – Benjamin Gruenbaum Jun 19 '16 at 15:39
  • @BenjaminGruenbaum Thank you. Never used it in real life until now - as well as 51% of BB API. But `settle` is/was a documented method with expected outcome, which is important for established API. In my particular case (the question wasn't supposed to reflect this) `promises` are native Promises, so they should be BB-ified first to be mapped to `reflect`. I guess this won't be a problem for `Bluebird.settle`. – Estus Flask Jun 23 '16 at 00:34
1

You can use Promise.all(), handle rejected Promise, return reason or other value to chained .then()

let promises = [
  Promise.resolve("resolved"),
  Promise.resolve("resolved"),
  Promise.reject("rejected")
]
, results = {resolved:[], rejected:[]}

, resolvedAndRejected = Promise.all(
  promises.map((p) => {
    return p.then((resolvedValue) => {
      results.resolved.push(resolvedValue);
      return resolvedValue
    }, (rejectedReason) => {
      results.rejected.push(rejectedReason);
      return rejectedReason
    })
  }));

resolvedAndRejected.then((data) => {
  console.log(data, results)
});
guest271314
  • 1
  • 15
  • 104
  • 177