0

I'm writing a MongoDB indexer and using Bluebird for my promises which works fine when I want to submit an array of indexes.

My problem is that, if one of the promises is rejected (ie, the first), the rest of the indexes are still called. I want to stop the execution of anything that's after a failure.

var arr = [{col1: 1}, {col2: 1}];

return bluebird.all(arr.map(function (index) {
    // This returns a promise
    return indexingFunction(index);
}));
MrSimonEmms
  • 1,321
  • 2
  • 16
  • 32

2 Answers2

1

You can use .each():

return bluebird.each(arr, function(item) {
  return indexingFunction(item);
});
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • The [doc I see](https://github.com/petkaantonov/bluebird/blob/master/API.md#eachfunction-iterator---promise) for Bluebird says that `bluebird.each()` takes an iterator, not an array. Am I missing something in the Bluebird doc or what you are using an undocumented capability? – jfriend00 Aug 21 '15 at 19:06
  • @jfriend00: _"Note that every instance promise method in the API has a static counterpart. For example `Promise.map(arr, fn)` is the same as calling `Promise.resolve(arr).map(fn)`"_ (right before [here](https://github.com/petkaantonov/bluebird/blob/master/API.md#core)) – robertklep Aug 21 '15 at 19:12
  • 1
    OK, thx. I'm just regularly frustrated with the Bluebird doc. They like to put statements like this some place and then assume that everyone reading any other section of the doc is entirely aware of that statement and its relevance to other sections. It's just a bad idea for technical documentation. As a sign of how incomplete the doc is, I often have to look at the code itself to find out what really is or isn't supported. – jfriend00 Aug 21 '15 at 19:18
  • @jfriend00 I can't disagree :-) – robertklep Aug 21 '15 at 19:19
0

First off you have to decide if you're running all your async operations in parallel or in sequence. If you launch all your operations in parallel, then all the requests are started at once and even though the first one fails, the others have already been sent so you can't stop them from being sent.

If you run your async operations in sequence where you issue one request and only once you get that response do you then run the next request, then you can stop any subsequent operations from being sent when one fails.

The code in your question, is launching all the requests in parallel because arr.map() is synchronous (it runs through the entire array all at once). Thus when the first response comes back, all the other requests have already been sent so you can't stop them.

There are a number of ways you can sequence items from an array. One common design pattern that works for all Promise libraries is to use .reduce():

var arr = [{col1: 1}, {col2: 1}];
arr.reduce(function(p, val) {
    return p.then(function() {
        return indexingFunction(val);
    });
}, Promise.resolve(result)).then(function() {
    // all finished successfully
}, function(err) {
    // finished with err
});

Bluebird also has some functions built in for working with collections:

Promise.map(arr, function(val) {
    return indexingFunction(val);
}, {concurrency: 1}).all().then(function(results) {
    // all results here
}, function(err) {
    // error here
});

FYI, you may also want to see this: ES6 Promises - something like async.each?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979