1

I have the following function that returns a Promise:

function someFn() {
    // Making use of Bluebird's Promise.all()
    return Promise.all([promise1, promise2]).then(function(results) {
        results.forEach(function(result) {
            return promiseReturningFn();  // Returns a Promise
        });
    }).catch(function(err) {
        console.log(err);
    });
}

I have two questions regarding this piece of code:

  1. In a scenario where any of the forEach() iteration's promiseReturningFn() rejects, will the rejection be caught by the catch() statement?
  2. If the first iteration's promiseReturningFn() rejects, will the iteration stop, meaning that the callback will not be called for the second element in the results array? If this is true, will control flow be passed to the catch() statement after this failure?
h_tm
  • 137
  • 1
  • 9
  • If you throw an error inside your `forEach` callback then it will reject the promise. If you want to return a promise, you need to use the classic `for` (or `while`) loop and not the `forEach` function. – SeregPie May 22 '17 at 22:38
  • No, you [cannot use `forEach` with promises](https://stackoverflow.com/q/37576685/1048572) – Bergi May 22 '17 at 23:24
  • 1
    You are looking for Bluebird's `Promise.map` or `Promise.each` – Bergi May 22 '17 at 23:25
  • @Bergi So, after taking a look at `Promise.map`, it seems like the following case, `Promise.map([promise1, promise2], promiseReturningMapper).then().catch();`, would pass control flow to the `catch()` statement after a rejection in any of the promises in the input array OR any of the promises returned by the mapper function. Is this correct? – h_tm May 23 '17 at 05:31
  • @h_tm Yes, exactly. – Bergi May 23 '17 at 05:56

1 Answers1

3

On your two questions:

  1. In a scenario where any of the forEach() iteration's promiseReturningFn() rejects, will the rejection be caught by the catch() statement?

No, because you do not return a promise in the then callback, but in the forEach callback, and the latter has no effect -- such a return value is lost in oblivion.

  1. If the first iteration's promiseReturningFn() rejects, will the iteration stop, meaning that the callback will not be called for the second element in the results array? If this is true, will control flow be passed to the catch() statement after this failure?

No, the iteration will not stop. You would not even know about a rejection synchronously, as this info only becomes available asynchronously (ignoring for a moment that bluebird allows synchronous inspection).

Here is probably how you should code it, assuming you allow the promiseReturningFn to already be executed on a resolved promise when the other one is not yet resolved:

function someFn() {
    // Making use of Bluebird's Promise.all()
    return Promise.all(
        [promise1, promise2].map(p => p.then(promiseReturningFn))
    }).catch(function(err) {
        console.log(err);
    });
}

Or, if you really need the array of promises to all resolve before you start executing promiseReturningFn for them, then do this:

function someFn() {
    // Making use of Bluebird's Promise.all()
    return Promise.all([promise1, promise2]).then(results => {
        return Promise.all(results.map(promiseReturningFn));
    }).catch(function(err) {
        console.log(err);
    });
}

In either case the answers to your two questions are now:

  • Yes, if either inner promise rejects, the catch callback will be executed.

  • No, the iteration will not stop, since the result of a promise is only known asynchronously, so by that time the loop (map) will already have executed completely.

To make the answer "Yes" to the second question and stop calls being made to promiseReturningFn when a previous returned promise rejects, you must serialise them, waiting for one promise to fulfil before invoking promiseReturningFn again:

function someFn() {
    // Making use of Bluebird's Promise.all()
    return Promise.all([promise1, promise2]).then(results => {
        return (function loop(i, p) {
            return i >= results.length ? p
                : p.then(_ => loop(i+1, promiseReturningFn(result[i])));
        })(0, Promise.resolve());
    }).catch(function(err) {
        console.log(err);
    });
}
trincot
  • 317,000
  • 35
  • 244
  • 286
  • In the first code sample, if I understand correctly, after the `map()` has executed I would end up with the equivalent to a call to `Promise.all([promiseReturningFn, promiseReturningFn])`, right? If I am right about this, the `then()` statement of the `Promise.all()` call would receive an array with the resolved promises from `[promiseReturningFn, promiseReturningFn]`. Is my understanding correct? – h_tm May 23 '17 at 02:43