Reputation: 105039
This is the pseudo scenario
| then (items) | then (items, actions)
getItems() | getActions(for:items) | apply(actions -> items)
:promise | :promise | model <= items
| | :synchronous
So in words:
This is somewhat the code I'm using
return itemsResource
.getItems(userId)
.$promise
.then(function(items) {
return actionsResource
.getActions(items.map(i => i.id)) // pseudo mapper code here
.$promise
.then(function(actions) { // THIS IS NESTED so it sees both promise results
return [items, actions];
});
})
.then(helper.spread(function(items, actions) {
applyActions(items, actions);
$scope.model.items = items;
return items;
}));
As you can understand I can't use $q.all
initially because second (actions) promise depends on results of the first one (items).
Why not return actions with items? Because I'm caching items for all users, so item fetching is really quick. This is similar to how Stackoverflow works. They just return questions regardless of user requesting them. Then they subsequently also request for preferred and ignored tags and apply to fetched questions. This scales very well, otherwise SO would require many more servers to handle just initial list of questions for all users, because every user's request would yield different results, so caching wouldn't make much sense.
Why not apply items to $scope
immediately after being fetched? This would eliminate the additional nested then
, that's true. but I'm not doing that because there are few more steps afterwards and every time one of the promises gets resolved at least one $digest
cycle is being executed. With many items (being rather complex objects) this can account for quite some processing. That's the reason I'm holding on to passing items to the view to the very last moment possible.
Is there a way to avoid the nested then
beside these two workarounds:
$scope
as soon as possibleI'd like to flatten my code as much as possible without introduction of additional resources if possible?
Upvotes: 2
Views: 133
Reputation: 49590
Unless I'm missing something, this should be fairly straightforward, no?
(I have simplified some of your internal function signatures for clarity)
itemsResource.getItems(userId)
.then(function(items) {
return $q.all({
items: items,
actions: actionResource.getActions(items)
});
})
.then(function(data) {
applyActions(data.items, data.actions);
$scope.model.items = data.items;
return data.items;
});
plunker for illustration
Upvotes: 2
Reputation: 82479
How about wrapping the results of the items->actions chain in a promise? Something like
return $q(function(resolve, reject) {
var outerItems;
itemsResource.getItems(userId).$promise
.then(getActions)
.then(function (actions) {
resolve([outerItems, actions]);
})
.catch(function (err) { reject(err); });
function getActions(items) {
outerItems = items;
return actionsResource.getActions(items).$promise
}
}).then(function (itemAndActions) {
var items = itemsAndActions[0],
actions = itemsAndActions[1];
return helper.spread(function (items, actions) {
applyActions(items, actions);
$scope.model.items = items;
return items;
})
});
Upvotes: 0