How do I add a callback function after an async forEach Loop?
Here is some better context:
$scope.getAlbums = function(user, callback) {
$scope.albumsList.forEach(function (obj, i) {
$scope.getAlbum(user, obj.id, function(value){
$scope.albums.push(value);
});
});
// callback(); ???
};
$scope.getAlbums('guy123', function(){
// forEach loop is all done!
console.log($scope.albums)
});
Controller:
.controller('Filters', ['$scope','Imgur', function($scope, Imgur) {
$scope.getAlbum = function(user, id, callback) {
Imgur.album.get({user:user, id:id},
function(value) {
return callback(value.data);
}
);
}
$scope.getAlbums = function(user, callback) {
// Callback function at end of this forEach loop?
// Request is async...
$scope.albumsList.forEach(function (obj, i) {
$scope.getAlbum(user, obj.id, function(value){
$scope.albums.push(value);
});
});
};
)]};
Service:
.factory('Imgur', function($resource) {
return {
album : $resource('https://api.imgur.com/3/account/:user/album/:id')
}
});
You can see, the callback function is called when another function is completed. This is a nice feature in an asynchronous environment like Angular ( this is the main usage of a callback in Angular) ! As you can see the callback function is passed as function parameter in Angular !
JavaScript Callbacks. ❮ Previous Next ❯. "I will call back later!". A callback is a function passed as an argument to another function. This technique allows a function to call another function. A callback function can run after another function has finished.
Another async is needed in the forEach so that we have the list of repos for each user to manipulate. We call it as: This and the promises approach are my favorites since the code is easy to read and change.
Then, we can update our example to use our asyncForEach method: We’re getting closer! Actually, our asyncForEach returns a Promise (since it’s wrapped into an async function) but we are not waiting for it to be done before logging ‘Done’. Let’s update our example again to wrap our execution into an async method:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With