Produce a promise which depends on recursive promises

I have an array of integer ids, such as

var a=[1,2,3,4,5]

and I have a need to perform asynchronous remote calls for each of these ids. Each call is a WebAPI request performed using $resource and presented as promise.

I need to create a function that takes array of these IDs, then initializes recursive promises chain. The chain should result in consequential webapi calls for each of IDs, one by one. These calls should not be parallel but chained.

The function in question returns itself a "main" promise that should be resolved or rejected based on result of asynchronous web calls. That is, if some promise in the recursuion is rejected due to disconnection from server, the main promised also should fail. In normal case, "main" promise must resolve at the point when all requests are completed.

How can I accomplish that in angularjs?


Solution 1:

You use reduce over the array to chain the promises together. There is no need make this recursive.

// for angularjs: var Q = $q.when;
var p = a.reduce(function(prev, el) {
    return prev.then(function(arr) {
        return makeRequest(el).then(function(res) {
             return arr.concat([res]);
         });
    });
}, Q([]));

Solution 2:

This is actually a very reasonable request.

The tool we use for this in libraries without a specific .each control is .then:

var a = [1,2,3,4,5];
var p = makeRequest(a.shift()); // use first element
a.forEach(function(el){
    p = p.then(function(result){
        return makeRequest(el);
    });
});

p.then(function(){
    // all requests done
});

Note that I assume you have a makeRequest method here that makes a single request and returns a promise that fulfills when that request is done.

It's also possible to return the results of the requests if you need that using a secondary array:

var a = [1,2,3,4,5];
var results = Array(a.length);
var p = makeRequest(a.shift()).
        then(function(res){ results[0] = res;}); // use first element
a.forEach(function(el,i){
    p = p.then(function(result){
        results[i] = result;
        return makeRequest(el);
    });
});
p = p.thenResolve(results); // resolve with results, in BB that'd be p.return(results)

p.then(function(results){
    // all requests done
    console.log(results); // array of response values
}).catch(function(e){
    // single failure
});

Using the v2 branch of the Bluebird library, you can do:

 Promise.each([1,2,3,4,5],makeRequest).then(function(results){
      // access results
 });