SET001
SET001

Reputation: 11718

How does Angular $q.when work?

Can some one explain me how does $q.when work in AngularJS? I'm trying to analyse how $http work and found this:

var promise = $q.when(config);

And here is config object from Chrome console:

Object {transformRequest: Array[1], transformResponse: Array[1], cache: Object, method: "GET", url: "/schedule/month_index.html"…}
cache: Object
headers: Object
method: "GET"
transformRequest: Array[1]
transformResponse: Array[1]
url: "/schedule/month_index.html"
__proto__: Object

What happens next? How this object get's resolved or rejected?

Upvotes: 105

Views: 77194

Answers (2)

Nate Anderson
Nate Anderson

Reputation: 21094

I know this question is old, and the accepted answer's great, but there's so many followup questions I wanted to consolidate some answers, even if only a reference for myself.

"How this object get's resolved or rejected?"

$q.when returns a Promise object. To get the Promise's resolved value, use promise .then(callbackFunction) syntax...

let i = 1;
$q.when(i).then(function(resolvedValue){
    console.log(resolvedValue === i);
});

More on Promises here

"If you pass a value to it - but what if I passing object?"

Same behavior if passing anything except a Promise/"then-able", including integers, strings, arrays, objects, and even functions. Here are some examples:

// an object...
let o = {name:'Nate'};
$q.when(o).then(function(resolvedValue){
     console.log(resolvedValue === o);
});
//...an array...
let a = ['Nate'];
$q.when(a).then(function(resolvedValue){
     console.log(resolvedValue === a);
});

"What if I pass a function that returns a promise?"

If you pass a function without executing the function, it's just like passing any other object except a Promise; the resolvedValue will be equal to the function itself, regardless of the return value of the function.

let i = 1;
let f = function(){ return Promise.resolve(i); };
$q.when(f).then(function(resolvedValue){
     // the resolvedValue is the *function* `f` itself!...
     console.log(resolvedValue === f);

     // the resolvedValue is NOT the function's return value, even if the function returns a Promise
     console.log(resolvedValue !== i);
});

Calling the function first $q.when(func) vs $q.when(func())

Just as @jrista says, if you have a function that returns a Promise, and you call that function before passing to $q.when, then you're really just passing a Promise into $q.when.

let i = 1;
let f = function(){ return i; };
$q.when(f); /* the resolved value will be the function `f` itself */
$q.when(f()); /* the resolved value will be based on the function `f`'s  return value (the function `f` is no longer relevant, because you called it*/

$q.when(func) vs $q.when(..).then(func)

Be careful not to confuse passing the function into the Promise/$q.when(f) (in which case the Promise's resolved value will be equal to the function):

let f = function(){ /*...*/ };
$q.when(f)

vs using the function in the .then() (in which case the function will be executed and passed the resolved value of the Promise):

let f = function(){ /*...*/ };
$q.when(/*...*/).then(f);

What if I pass a Promise/"then-able"?

As the docs and the other answers say, passing a Promise object (or any "then-able") is special. Rather than resolving to the same Promise object you passed, $q.when will resolve the the Promise's resolved value.

let i = 1;
let p = Promise.resolve(i); 
$q.when(p).then(function(resolvedValue){
     // the resolvedValue is *NOT* the promise `p` itself...
     console.log(resolvedValue !== p);

     // the resolvedValue **IS* the promise's *resolved* value:
     console.log(resolvedValue === i);
});

Why use $q.when?

As the docs say:

useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

Consistent return types

Let's give an example of "might or might not be a promise"; maybe you cache the results of $http() get request. Now you can have a function that returns the cached value, or returns an asynchronous $http() request to get the value.

var cache = {};
var get_value_from_cache_or_$http_get = function (resource_id){
    var cached_value = cache[resource_id];
    if (cached_value){
        // if you cached the value, you don't need to make another $http request...
        // HOWEVER you still want to return a Promise/then-able, 
        // so the code calling your function `get_value_from_cache_or_$http_get` 
        // gets a consistent return type (i.e. something "then-able")
        return $q.when(cached_value);
    } else {
        return $http.get(resource_id);
    }
}

Now the function get_value_from_cache_or_$http_get will always return a "then-able", regardless of whether it fetched something that was a promise (i.e. $http.get), or might not be a promise (i.e. fetched from cache, but wrapped in $q.when)

$scope updates in the UI

I think this part of the docs is especially important (maybe the most important reason that I use $q.when)...

$q is integrated with the $rootScope.Scope Scope model observation mechanism in AngularJS, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.

In other words, I find that when I get the resolved value of a normal Promise and assign to $scope, I don't always see the change in the UI immediately (I must wait for the next $digest()).

let p = Promise.resolve('update');
p.then(function(resolvedValue){ $scope.val = resolvedValue; })

If I wrap the promise in $q.when, I see the change in UI as soon as the resolved value is assigned to $scope

let p = Promise.resolve('update');
$q.when(p).then(function(resolvedValue){ $scope.val = resolvedValue; })

$q.when vs $q.resolve

Note these functions are the same $q.when === $q.resolve, as the docs say:

[$q.resolve is an] alias of when to maintain naming consistency with ES6.

Upvotes: 0

Derek Ekins
Derek Ekins

Reputation: 11391

Calling $q.when takes a promise or any other type, if it is not a promise then it will wrap it in a promise and call resolve. If you pass a value to it then it is never going to be rejected.

From the docs:

Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

Upvotes: 117

Related Questions