104

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?

martin
  • 93,354
  • 25
  • 191
  • 226
SET001
  • 11,480
  • 6
  • 52
  • 76
  • $q implements promise patterns its just simple wrapper around callbacks in javascript .So when callback is sucessfully fired the promises gets resolved – Ajay Beniwal May 27 '13 at 10:32
  • 1
    @Ajaybeniwal, but in this case object passed, not callback. How it resolved/reject in case when object passed, not callback? – SET001 May 27 '13 at 10:58

2 Answers2

117

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.

ruffin
  • 16,507
  • 9
  • 88
  • 138
Derek Ekins
  • 11,215
  • 6
  • 61
  • 71
  • `If you pass a value to it` - but what if I passing object? – SET001 Jun 13 '13 at 12:31
  • 13
    value, object, array it is all the same. – Derek Ekins Jun 13 '13 at 13:35
  • 3
    what if I pass a function that returns a promise? does it mean the then callback of it will be passed the promise result param send by resolve? – Onur Topal Oct 17 '14 at 11:18
  • 3
    Onur, when you say pass a function, do you mean as a function object: `$q.when(myfunc)`, or by invoking the function your passing in: `$q.when(myfunc())`? I don't know exactly what the former will do...the latter will invoke `myfunc()` first, and pass the returned promise|value to `.when()`. – jrista Mar 24 '15 at 03:14
0

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.

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135