How do I add a callback function after an async forEach Loop?
Here is some better context:
$scope.getAlbums = function(user, callback) {
It looks like deferred http://docs.angularjs.org/api/ng.$q and specifically chaining promises could be useful here.
$scope.getAlbums = function(user, callback) {
var promiseArr = [];
$scope.albumsList.forEach(function (obj, i) {
var anHttpPromise =
$scope.getAlbum(user, obj.id, function(value){
$scope.albums.push(value);
});
promiseArr.push(anHttpPromise);
});
$q.all(promiseArr).then(function(){
// This callback function will be called when all the promises are resolved. (when all the albums are retrived)
})
};
$scope.getAlbum = function(user, id, callback) {
var anHttpPromise = Imgur.album.get({user:user, id:id},
function(value) {
return callback(value.data);
}
);
return anHttpPromise;
}
In the above code:
getAlbum
is made to return an promise.getAlbums
list$q.all
$q.all
method instead returns an final promise whose callback
function will be triggered once all the promises in the array are
resolved.As Andrew said usage of $q
and the deferred object should allow you to accomplish your goal.
You want to use $q.all()
This will make sure all of your promise objects are resolved and then you can call your call back in .then()
function MyCtrl($scope, $q, $http) {
$scope.albumsList = [{
id: 1,
name: "11"
}, {
id: 2,
name: "22"
}
];
$scope.albums = [];
$scope.getAlbum = function(user, id, callback) {
return $http.get("https://api.imgur.com/3/account/" + user + "/album/" + id).success(
function(value) {
return callback(value.data);
}
);
}
$scope.getAlbums = function (user, callback) {
var prom = [];
$scope.albumsList.forEach(function (obj, i) {
prom.push($scope.getAlbum(user, obj.id, function(value){
$scope.albums.push(value);
}));
});
$q.all(prom).then(function () {
callback();
});
};
$scope.getAlbums('guy123', function () {
alert($scope.albums.length);
});
}
Example with this on jsfiddle
Works but not with $http
calls
With the deferred object you gain access to a promise where you can change successive then()
calls together. When you resolve the deferred object it will execute the foreach and then execute your call back function you supplied. I tried to simplify your example a bit further so it would work in jsfiddle.
function MyCtrl($scope, $http, $q) {
$scope.albumsList = [{
id: 1,
name: "11"
}, {
id: 2,
name: "22"
}];
$scope.albums = [];
$scope.getAlbums = function (user, callback) {
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function () {
$scope.albumsList.forEach(function (obj, i) {
$scope.albums.push(obj);
});
}).then(function () {
callback();
});
deferred.resolve();
};
$scope.getAlbums('guy123', function () {
alert($scope.albums.length);
});
}
Example on jsfiddle
A bit more reading on deferred and promises.
Using the $resource
promise PR commit slated for 1.1.3, I was able to wrap $resource
calls with $q and control the flow of their async behavoir.
$scope.getAlbum = function(user, id, callback) {
var promise = Imgur.album.get({user:user, id:id}).$promise.then(
function( value ){
return callback(value.data);
},
function( error ){
//Something went wrong!
}
)
return promise;
}
$scope.getAlbums = function(user, callback) {
var prom = [];
$scope.albumsList.forEach(function (obj, i) {
var promise =
$scope.getAlbum(user, obj.id, function(value){
$scope.albums.push(value);
});
prom.push(promise);
});
$q.all(prom).then(function () {
callback();
});
};
$scope.getAlbums(user, function(){
// Totally works, bro.
console.log($scope.albums);
});