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
}
}
source to share
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.
source to share
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
source to share
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);
}
source to share
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/
source to share