Reputation: 17524
I have a list of records I want to process in sequence. A "root" promise needs to be passed back to be added as part of another parent chain.
processCategories = function(categories) {
var deferred = $q.defer();
var promise = deferred.promise;
// Process each category in sequence
angular.forEach(categories, function (data, classIndex) {
promise = promise.then(doSomethingAndReturnAPromise(categories, data, classIndex));
if (classIndex === categories.length - 1) {
// Last category has been reached, resolve this unit of work
promise.then(function() {
deferred.resolve(categories);
});
}
});
// Pass the root promise back to the chain
return promise;
}
doSomethingAndReturnAPromise = function(categories, data, index) {
console.log('item : ' + index + ' = started');
var deferred = $q.defer();
executeAnAsynchronousCall(function () {
console.log('item : ' + index + ' = done');
deferred.resolve(categories);
}, function(status, text) {
console.log('item : ' + index + ' = error');
// Handle error here
}, data);
console.log('item : ' + index + ' = returned');
return deferred.promise;
}
I'm getting some errors from my asynchronous call because the executions are occurring at the same time (and not sequentially). I added the logging above and observed the following:
2014-08-08 11:38:02.702 item : 1 = started
2014-08-08 11:38:02.702 item : 1 = returned
2014-08-08 11:38:02.702 item : 2 = started
2014-08-08 11:38:02.703 item : 2 = returned
2014-08-08 11:38:02.703 item : 3 = started
2014-08-08 11:38:02.704 item : 3 = returned
2014-08-08 11:38:02.704 item : 4 = started
2014-08-08 11:38:02.705 item : 4 = returned
2014-08-08 11:38:02.705 item : 5 = started
2014-08-08 11:38:02.706 item : 5 = returned
2014-08-08 11:38:02.707 item : 6 = started
2014-08-08 11:38:02.707 item : 6 = returned
2014-08-08 11:38:02.708 item : 7 = started
2014-08-08 11:38:02.709 item : 7 = returned
2014-08-08 11:38:02.709 item : 8 = started
2014-08-08 11:38:02.710 item : 8 = returned
2014-08-08 11:38:02.716 item : 2 = error
2014-08-08 11:38:02.718 item : 3 = error
2014-08-08 11:38:02.719 item : 4 = error
2014-08-08 11:38:02.720 item : 5 = error
2014-08-08 11:38:02.722 item : 1 = done // (This has finished, the server is now expecting #2)
2014-08-08 11:38:02.723 item : 6 = error
2014-08-08 11:38:02.735 item : 8 = error
2014-08-08 11:38:02.754 item : 7 = error
I was expecting the .then
to ensure the promises are run in sequence, but each time I run the code it appears they are all being fired at once.
Can anyone see where I'm going wrong here?
Upvotes: 1
Views: 209
Reputation: 30098
Change processCategories()
to something like this:
processCategories = function(categories) {
var promise, deferred;
// Process each category in sequence
angular.forEach(categories, function (data, classIndex) {
if(promise) {
promise = promise.then(function() {
return doSomethingAndReturnAPromise(categories, data, classIndex);
});
} else
promise = doSomethingAndReturnAPromise(categories, data, classIndex);
});
if(promise) {
promise.then(function() {
return categories;
});
} else {
deferred = $q.defer();
promise = deferred.promise;
deferred.resolve(categories);
}
// Pass the last promise
return promise;
}
Upvotes: 0
Reputation: 276306
Your second line is invoking the function rather than scheduling it to be invoked later
Change:
promise = promise.then(doSomethingAndReturnAPromise(categories, data, classIndex));
To:
promise = promise.then(doSomethingAndReturnAPromise.bind(this,categories, data, index));
Or a full blown anonymous function:
promise = promise.then(function(){
return doSomethingAndReturnAPromise(categories, data, classIndex));
});
As a side note, if you need the promise to resolve with the categories, it would be simpler to change the return value directly rather than put that check in the forEach.
processCategories = function(categories) {
return categories.reduce(function (prev, data, index) {
return prev.then(function(){
return doSomethingAndReturnAPromise(categories, data, index));
});
}, $q.when()).then(function(){
return categories;
});
}
Upvotes: 3