问题
The code I have loops through 10 items in an array, makes a request for each item, then pushes the returned data to an array. Everything runs fine until the $q.all
line.
details.getDetails = function(idSet, pageNum) {
var page = idSet[pageNum],
mainDeferred = $q.defer(),
promises = [],
combinedItems = [];
for(var i=0; i<page.length; i++){
var deferred = $q.defer();
ngGPlacesAPI.placeDetails({placeId: page[i][i]})
.then(function(data){
combinedItems.push(data);
console.log(combinedItems); /// This shows the objects being pushed into the array
deferred.resolve();
});
promises.push(deferred.promise);
}
console.log(promises); /// This shows the 10 promises
$q.all(promises).then(function() { /// Nothing after this line runs
console.log('test', mainDeferred.promise); /// This logs nothing
mainDeferred.resolve(combinedItems);
});
return mainDeferred.promise;
};
回答1:
This is a pretty common error when dealing with async in javascript.
The question should should ask is: "when the resolver (the function you pass to .then
) looks up the variable deferred
, what variable does it get back?". Then answer is, they all reference the very last deferred
you declare.
The problem is, you're declaring deferred
outside of your resolve function. Remember how javascript looks up variables:
- Is the variable (in our case,
deferred
) available in the immediate function scope? (in our case, no) - Traverse up parent scopes until we find a variable with the given name.
By the time the resolver fires, you've redeclared deferred
10 times, each declaration overwriting the previous one! So each time a resolver fires, it actually resolves the same deferred
!
The answer is to wrap your deferred
declaration in a closure:
for(var i=0; i<page.length; i++){
(function(){
var deferred = $q.defer();
ngGPlacesAPI.placeDetails({placeId: page[i][i]})
.then(function(data){
combinedItems.push(data);
console.log(combinedItems); /// This shows the objects being pushed into the array
deferred.resolve();
});
promises.push(deferred.promise);
})()
}
But really, you can simplify your whole program and avoid the deferreds. Let me know if this makes sense to you!:
details.getDetails = function(idSet, pageNum) {
var page = idSet[pageNum];
// generate an array from 0 to page.length
var items = Array.apply(null, { length: page.length })
var promises = items.map(function (i) {
return ngGPlacesAPI.placeDetails({placeId: page[i][i]});
})
return $q.all(promises)
};
来源:https://stackoverflow.com/questions/35978172/trouble-with-asynchronous-events-in-angular