I still can't understand the role of using the $q service, (what exactly will it add) if you want to create a service that need to call only one API via http , in this situation I don't know why shouldn't I just do the following (without using $q) :
this.getMovie = function(movie) {
return $http.get('/api/v1/movies/' + movie)
.then(
function(response) {
return {
title: response.data.title,
cost: response.data.price
});
},
function(httpError) {
// translate the error
throw httpError.status + " : " +
httpError.data;
});
};
4 Answers 4
Very good question and one very few people appreciate the answer to.
Consider this:
this.getMovie = function(movie) {
return $http.get('/api/v1/movies/' + movie);
};
Great code but these rules apply:
$http will resolve for 2xx responses and will otherwise reject. sometimes we don't want this. we want to reject on resolution and resolve on rejection. This makes sense when you consider a HEAD request to check the existence of something.
A HEAD request to /book/fred
returning 200 shows book fred exists. But if my function is testing whether book fred is unique, it is not and so we want to reject on a 200. this is where $q comes in. Now I can do this:
var defer = $q.defer();
$http.head('/book/fred').then(function(response) {
// 2xx response so reject because its not unique
defer.reject(response);
}).catch(function(failResponse) {
defer.resolve(failResponse);
});
return defer.promise;
$q gives me total control of when I reject AND when I resolve.
Also, $q allows me to reject or resolve with any value. So if I am only interested in some of the response I can resolve with just the bit I am interested in.
Finally, I can use $q to turn non-promise based code into a promise.
var a = 5;
var b = 10;
var defer = $q.defer();
var resolve(a+b);
return defer.promise;
Bosh, if I need a promise as my return value then I've got one.
This is also great when mocking for unit tests.
Comments
AngularJS services such as $http
, $timeout
, $resource
, etc. use the $q
service internally to generate their promises. With those services there generally is no need to inject the $q
service. In fact if you see $q.defer
being used with those services, you should be asking Is this a "Deferred Antipattern"?.
There are some methods of the $q
service that are useful in certain circumstances.
The $q.all
method is used to wait on several promises.
var promise1 = $http.get(url1);
var promise2 = $http.get(url2);
$q.all([promise1, promise2]).then( responseArray ) {
$scope.data1 = responseArray[0].data;
$scope.data2 = responseArray[1].data;
}).catch( function ( firstError ) {
console.log(firstError.status)
});
The .catch
method can be used to convert a rejected promise to a fulfilled promise. And vice versa with the .then
method. No need to use $q.defer
for that. For more information, see Angular execution order with $q
.
The $q.when
method is useful for generating a promise from unknown sources.
var promise = $q.when(ambiguousAPI(arg1));
The $q.when
method creates a $q
promise in all cases whether ambiguousAPI
returns a value, a $q
promise, or a promise from another library.
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
To summarize: The $q
service is used to create a promise, so when using service (like $http
,$timeout
,$resource
,etc.) that already return promises you generally don't need to use the $q
service.
Comments
In this case you certainly don't need it because $http.get
itself returns a promise. But for example if you perform async call only on some condition it is useful
function acyncService () {
if (dataLoaded) return $q.resolve(data);
return $http.get('path/to/load/data');
}
In this case even if you do not perform async call, you still can use
acyncService().then(function(data){
console.log(data);
});
This is only one of many examples. It is also useful to use $q promises
when you do async requests with other libs like AWS SDK
for instance.
3 Comments
$http
service uses $q
service to return a promise.$http
does an asynchronous call. So, ideally if you want to get the value of the response in the url, you should use a '$scope' variable to store it.
$http("your url").then(function(response){
//This is the success callback
$scope.movies = response.data;
});
Comments
Explore related questions
See similar questions with these tags.