Reputation: 1582
I want to resolve a recursive data structure before calling a controller, but I'm struggling with how to do this.
my current code looks like this:
region.loadChildren = function() {
region.children = [];
return region.resource.all('children').getList().then(function(subs) {
angular.forEach(subs, function(sub) {
sub.loadChildren();
region.children.push(sub);
});
}, errorCallback
);
};
can I somehow "collect" the promises returned by sub.loadChildren() and combine them?
Upvotes: 1
Views: 252
Reputation: 48968
Use $q.all
to consolidate promise collections and be sure to return them for chaining.
region.loadChildren = function() {
//return for chaining
return region.resource.all('children').getList().then(function(subs) {
var children = [];
angular.forEach(subs, function(sub) {
var p = sub.loadChildren();
children.push(p);
});
//return for chaining
return $q.all(children);
);
};
Because calling the .then
method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
For more information, see AngularJS $q service API Reference -- chaining promises.
Be aware that $q.all
is not resilient. If one of the promises is rejected, $q.all
will resolve rejected with the first error.
Upvotes: 1
Reputation: 2266
$q.all is youre solution -
region.loadChildren = function() {
region.children = [];
return region.resource.all('children').getList().then(function(subs) {
var promises = subs.map(subs, function(sub) {
var promise = sub.loadChildren();
region.children.push(sub);
return promise;
});
var allPromisesInOnePromise = $q.all(promises); // a regular promise which will resolve when all of the promises will resolve
}, errorCallback
);
};
you can read more about promises here and the $q.all function here- https://docs.angularjs.org/api/ng/service/$q
Upvotes: 1
Reputation: 339816
You can replace your current function with one that creates the initial array into which the promises will be accumulated, and then pass that to a modified version of your original function that does the real work:
region.loadChildren = function() {
var tmp = [];
this.loadChildrenHelper(tmp);
this.children = tmp;
}
region.loadChildrenHelper = function(accum) {
return region.resource.all('children').getList().then(function(subs) {
angular.forEach(subs, function(sub) {
sub.loadChildrenHelper(accum);
accum.push(sub);
});
}, errorCallback
);
};
and then use $q.all
on the resulting array
Upvotes: 0
Reputation: 18803
Yep, you can use $q.all
to gather the promises into a single promise that resolves when each promise in the list has resolved.
var promises = []
angular.forEach(subs, function(sub) {
promises.push(sub.loadChildren());
region.children.push(sub);
});
$q.all(promises).then(function(values) {
// Finished
})
Upvotes: 2