Using the $q service with angular
Asked Answered
A

4

5

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;
    });
};
Abydos answered 1/4, 2016 at 20:46 Comment(0)
Z
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.

Zambrano answered 1/4, 2016 at 21:7 Comment(0)
C
2

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.

Coitus answered 2/4, 2016 at 3:59 Comment(0)
D
1

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.

Debbidebbie answered 1/4, 2016 at 20:58 Comment(3)
I am trying to understand your answer so please correct me if I am wrong . You are saying that $q is used to create a promise, so when using acyncService (like $http.get) that already returns a promise you don't need to use $q ?Abydos
Correct. In fact $http service uses $q service to return a promise.Debbidebbie
Typical use-case of this combination of $http and $q is would be an Angular service which does a one-time load (say, a lookup table of static data from an API) but that subsequent calls will short-out and return the already-loaded/cached data.Willette
S
0

$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;
      });
Shigella answered 1/4, 2016 at 20:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.