How can I keep the count of a for loop with promises returned in no particular order?

I am making a series of http requests and I need to post the results to a list object when they return. I am using angular promises.

Since promises are only resolved after the for loop ends, they all end up in the last index of the list.

for (var i = 0;i < list.length; i+=1) {
    Promise.do(action).then(function(result) {
        list[i] //i is always at last index because the for loop has already completed
    }
}

      

+3


source to share


5 answers


I would try to use $q.all

for this:

var promises = [];

for (var i = 0; i < list.length; i += 1) {
    promises.push(Promise.do(action));
}

$q.all(promises).then(function(results) {
    console.log(results);
});

      



From the documentation:

Returns one promise to be resolved using an array / hash of the value, each value matching the promise with the same index / key in the promises array / hash. If any of the promises are resolved with rejection, then that promise will be rejected with the same rejection value.

+1


source


Bind the index as an argument to the function receiving the result:



for (var i = 0;i < list.length; i+=1) {
    Promise.do(action).then((function(index, result) {
        list[index]
    }).bind(null, i));
}

      

+3


source


Don't use a canned loop. Use Array.forEach instead . Every time the function provided for forEach is called, you get a new closure and therefore a new copy of i

+2


source


Agree with Bergi's point, this looks like a notorious problem. However, I typed in my answer before ticking off Berga's remark, so here are a couple of suggestions for a solution:

for (var i = 0;i < list.length; i+=1) {
    var callback = function(result) {
        list[arguments.callee.i]...
    }
    callback.i = i;
    Promise.do(action).then(callback);
}

      

OR

for (var i = 0;i < list.length; i+=1) {
    var callback = function(n) {
        return function(result) {
            list[n]...
        };
    }(i);
    Promise.do(action).then(callback);
}

      

0


source


You can call the next loop in the Promise.then block like this:

function do_thing(i) {
    // Are we finished? If so, return.
    if (i == list.length) return;
    Promise.do(action).then(function(result) {
        list[i]
        // Do the next thing, only once the previous thing completed.
        do_thing(i+1)
    }
}
// Start at the first thing.
do_thing(0)

      

The problem is that you lose the benefit of concurrent asynchronous calls - everything gets serialized - so it will be slower. With jQuery promise, you can attach an index to the promise object ... will this work in angular?

for (var i = 0;i < list.length; i+=1) {
    var p = Promise.do(action)
    p.index = i
    p.then(function(result) {
         // this.index is the value of i so do...
         list[this.index] 
    }
}

      

Here is a fiddle showing what's in the jQuery flavor: http://jsfiddle.net/sifriday/0v6vr77y/

-1


source







All Articles