We're rewriting some parts of our application. We're trying to avoid the promise anti-pattern and do things properly, but I'm not certain that we did so.
More precisely, we are rewriting the resolver of a state using ui-router.
.state( 'catalogue', {
// skipped on purpose not relevant content
resolve :
boxCreateData : function( $stateParams, $q, Basket, Customer ) {
var defer = $q.defer(),
basket = null,
customer = null;
Basket.fromApi( $stateParams.id )
.then( function( basket ) {
Customer.fromApi( basket.getCustomer() )
.then( function( customer ) {
defer.resolve( { basket : basket, customer : customer } );
});
});
return defer.promise;
}
});
Basket.fromApi
and Customer.fromApi
are static methods of their corresponding "class"es, Basket
and Customer
. Both implementations are like this (more or less; it's not the whole code, but it shows the concept. Customer.fromApi
is the same):
Basket.fromApi = function( id ) {
var defer = $q.defer();
boxesResource.get( { id : id }, function( response ) {
// various manipulation here with the response...
defer.resolve( new Basket( ... ) );
});
return defer.promise;
};
Note we are using the get
method of $resource
.
The major issue here is that until we have the basket, we can't query the customer (because customer ID comes in the basket object).
We are querying first the basket with the basket ID. Once we have it, we can get the customer ID from the basket object and then, retrieve the customer object.
I just don't like the then
pyramid. I'm not sure if we are missing something or in this case, there is no other way to do it.
Is there a cleaner way to accomplish the same thing?
-
\$\begingroup\$ I can't quite tell from the question itself, so to clarify: This code works, but either looks ugly or doesn't work as well as you'd like it to. \$\endgroup\$anon– anon2015年06月12日 14:31:46 +00:00Commented Jun 12, 2015 at 14:31
-
\$\begingroup\$ I know this code works, its just Im not quite satisfied about having two nested promises. I think is something that it must be avoided. An anti-pattern. Or maybe its not. I just want to know if this is the proper way or not. Is there any way that allow me not to nest this promises ? \$\endgroup\$avcajaraville– avcajaraville2015年06月12日 14:48:19 +00:00Commented Jun 12, 2015 at 14:48
1 Answer 1
You shouldn't use $q.defer
unless you have to.
Here you can just return the promise:
Basket.fromApi = function (id) {
return boxesResource.get({
id: id
}, function (response) {
// various manipulation here with the response...
return new Basket(...));
});
};
You could really clean up the resolve
by creating a new service:
State
.state('catalogue', {
resolve: {
boxCreateData: function($stateParams, boxCreateService) {
return boxCreateService.getBoxCreateData($stateParams.id);
}
}
})
Service
app.service('boxCreateService', function(Basket, Customer) {
this.getBoxCreateData = function(id) {
return Basket.fromApi(id)
.then(function (basket) {
return Customer.fromApi(basket.getCustomer())
.then(function (customer) {
return {
basket: basket,
customer: customer
};
});
});
};
});
There's not much you can do to avoid two nested promises here. You need the customer ID before you can get the customer.