Why do we prefer using $q in angular instead of $http [duplicate]
Asked Answered
L

3

15

I am currently using $q service from angular to make API calls like this:

var deferred = $q.defer();
$http.get(config.apiHost + details.url)
    .success(function (data) {
        deferred.resolve(data);
    }).error(function (msg) {
        deferred.reject(msg);
    });
return deferred.promise;

but we can also use this approach also without using $q:

return $http.get(config.apiHost + details.url)
    .success(function (data) {
        return data;
    }).error(function (msg) {
        return msg;
    });

and as $http itself returns the promise, I can also use more simplified approach:

$http.get(config.apiHost + 'posts')
        .success(function (data) {
            console.log(data)
        }).error(function (msg) {
            console.log(msg);
        });

So what is the difference between all these specially between $q and $http, as both returns promise and both are async ? Does angular provides some additional functionality with $q ? I am not able to find any good answer.

Laszlo answered 28/6, 2016 at 15:19 Comment(6)
You can return the promise with other asynchronous operations with $q. $http returns a promise from AJAX calls.Garbers
other operations like ?Laszlo
Like reading files or if you need to use $timeoutGarbers
$q is mainly only used for compatibility with libraries that don't support promises by default and when you can't rely on a native implementation of Promise. There's no reason (for you) to use it otherwise. An example would if you wanted to make a promise-based $timeout.Alcaic
Ok, that means for API calls we can simply use $http instead of $q as they both returns promise. $q is useful only when some libraries do not support promises natively.Laszlo
@BhushanGoel yes, I suppose that would be the simplest way to explain itGarbers
A
4

$q is mainly only used for compatibility with libraries that don't support promises by default and when you can't rely on a native implementation of Promise (for example - in older browsers like IE9). There's no reason (for you) to use it otherwise. An example would if you wanted to make a promise-based $timeout. $http itself uses $q under the hood for these exact reasons.

Unlike what other (since deleted) answers have suggested, you do not need to use $q in order to "store" the result of the $http promise. I would not recommend storing the promise at all (as this tends to lead to spaghetti code), but if you must absolutely do this, you can just store the resultant promise from $http; promises only ever execute once.

Any functions passed to then after a promise has resolved/rejected will be resolved on the next tick, without re-invoking the original action that created the promise in the first place - IOW, the result of the promise is memoized within that object.

Also note that promises chain, which is abit out of scope for this answer, but it essentially means that the following pieces of code are equivalent

function legacyGet() {
  const deferred = $q.defer()
  $http.get('http://www.google.com')
    .then((response) => deferred.resolve(Object.assign(response, {foo: bar}))
    .catch((error) => deferred.reject(error))
  return deferred.defer
}

function modernGet() {
  return $http.get('http://www.google.com')
    .then((response) => Object.assign(response, {foo: bar}))
}

To summarise: Your title is wrong. We don't prefer using $q, we only use it sparingly. Prefer to use an ES6 Promise unless you need to support browsers that don't have it and you can't use a polyfill.

Alcaic answered 28/6, 2016 at 15:31 Comment(0)
Y
9

$http uses $q, the first example is redundant, and so is the second. You just need to return the promise that $http.get returns:

return $http.get(config.apiHost + details.url);

The above is the same as your first piece of code.

In addition, return msg is not the same as deferred.reject(msg). The equivalent would be throw msg or return $q.reject(msg)

Another thing to note is that success and error are non-standard, you want to use then and catch.

Yardman answered 28/6, 2016 at 15:23 Comment(3)
I wouldn't say that success and error are "non-standard", just deprecated: docs.angularjs.org/api/ng/service/$httpCapitalist
@Capitalist they are non-standard if you're talking about the A+ promise specAlcaic
@DanPantry ah, yes, I was mistakenly thinking you were referring to the angular $http service. In regards to the spec, it is non-standard and almost certainly why the angular folks have deprecated it.Capitalist
A
4

$q is mainly only used for compatibility with libraries that don't support promises by default and when you can't rely on a native implementation of Promise (for example - in older browsers like IE9). There's no reason (for you) to use it otherwise. An example would if you wanted to make a promise-based $timeout. $http itself uses $q under the hood for these exact reasons.

Unlike what other (since deleted) answers have suggested, you do not need to use $q in order to "store" the result of the $http promise. I would not recommend storing the promise at all (as this tends to lead to spaghetti code), but if you must absolutely do this, you can just store the resultant promise from $http; promises only ever execute once.

Any functions passed to then after a promise has resolved/rejected will be resolved on the next tick, without re-invoking the original action that created the promise in the first place - IOW, the result of the promise is memoized within that object.

Also note that promises chain, which is abit out of scope for this answer, but it essentially means that the following pieces of code are equivalent

function legacyGet() {
  const deferred = $q.defer()
  $http.get('http://www.google.com')
    .then((response) => deferred.resolve(Object.assign(response, {foo: bar}))
    .catch((error) => deferred.reject(error))
  return deferred.defer
}

function modernGet() {
  return $http.get('http://www.google.com')
    .then((response) => Object.assign(response, {foo: bar}))
}

To summarise: Your title is wrong. We don't prefer using $q, we only use it sparingly. Prefer to use an ES6 Promise unless you need to support browsers that don't have it and you can't use a polyfill.

Alcaic answered 28/6, 2016 at 15:31 Comment(0)
P
0

In angular mostly all the services returns promises only, but there are some instances where you would like to create your own deferred object using $q.

Case 1

When you are using a library which does not support promise or you have created your own function and want to return a promise.

Case 2

When you are using any construct which by default returns a promise but you want to return a separate promise based on some on some condition.

Example: In angular $http returns a promise only but now if you want that if the response of this promise contains a particular value then only you want to return resolved else return failure then you need to create your own deffered object and need to resolve or fail it based on the value returned by $http response.

Prebendary answered 28/6, 2016 at 15:34 Comment(7)
You do not need to do that. Promises chain. You can just as easily do $http.get().then(...) and the value returned from then will be passed to the next .then in the chain. No need to use your own deferred object for this.Alcaic
I'm talking about the response of the $http..like if you want that if the response contains a certain value than only resolve the promise or just reject.Prebendary
This answer is inaccurate and is describing the common deferred anti-patternMansur
If you're suggesting you'd want to reject a promise based on the value of the response, yes, I'll grant you - that is one case where you would want to use $q (but you could still return $q.reject() instead of using the deferred antipattern)... but that seems like an incredibly narrow use-case.Alcaic
But it is a case right....otherwise angular services already return promises..why would you use deferred object over them.Prebendary
It's not really a case, no, as I said - you could just as easily use $q.reject() and return that as a result of a promise. And as mentioned in my answer, the only use case for the deferred pattern is when you can't support an ES6 Promise and the method you're working with doesn't return oneAlcaic
In your answer also you are doing the same thing... you are tweaking the response only...If this tweaking is not required then why would you use $q for that..simple $http would do..Prebendary

© 2022 - 2024 — McMap. All rights reserved.