Get state of Angular deferred? Get state of Angular deferred? angularjs angularjs

Get state of Angular deferred?


Update:

Due to refactoring of $q this is now possible although not documented:

promise.$$state.status === 0 // pendingpromise.$$state.status === 1 // resolvedpromise.$$state.status === 2 // rejected

Original:

Unlike most promise libraries (Bluebird,Q, when, RSVP etc), $q does not expose a synchronous inspection API.

There is no way to achieve this from the outside.

You have to call .then on the promise and code in that handler will run when the promise fulfills.


The answer to your question is: yes, there is a way. The other answers nicely cover the built-in limitations of $q. However, it's easy to add a state property to $q using the $provide service's decorator function.

  $provide.decorator('$q', function ($delegate) {    var defer = $delegate.defer;    $delegate.defer = function() {      var deferred = defer();      deferred.promise.state = deferred.state = 'pending';      deferred.promise.then(function() {        deferred.promise.state = deferred.state = 'fulfilled';      }, function () {        deferred.promise.state = deferred.state = 'rejected';      });       return deferred;    };    return $delegate;  });

Put this decorator inside of a config block, and all $q-instantiated deferred and promise objects will have a state property with the value pending, fulfilled, or rejected.

Check out this plunk


Skeptical?

you are effectively modifying $q itself, wrapping every deferred with another deferred

Actually this is not the case. $q's original defer() constructor is called exactly one time. It is simply decorated with additional functionality by internally attaching an event handler via then. [Note that an additional defer object is instantiated as a result of the additional then callback which is automatically created with each deferred object... which is to be expected because this is how angular works internally.]

this wouldn't work because promises shouldn't be created with deferred but chained from promises that are returned from apis

Note that this code will decorate every deferred (and thus promise object) which is created by the $q service. This means that any API which utilizes $q will be automatically decorated with the state property. So regardless of how you use $q, whether with some API or on it's own, this solution decorates both the deferred object and the promise, and I have provided the plunk to prove it.


Production-worthy?

This approach is unit testable, it's guaranteed not to break any application already using $q, and it's flexible in the sense that you could later add additional decorators to $q without modifying the old one(s).


Updated:

Unfortunately this doesn't looks like its possible with $q. You'll have to put this code inside your then method.

myPromise().then(function() {    // everything in here resolved},function() {    // everything in here rejected},function() {    // everything in here pending (with progress back)});

Other:

This is for the Q library not angular's $q but similar.

Angular is inspired by the Q library, check out the source, its actually not that scary. https://github.com/kriskowal/q/blob/v1/q.js

You can use myPromise.inspect().state there are ['pending', 'rejected', 'fulfilled']

You also have:

myPromise.isFulfilled();myPromise.isPending();myPromise.isRejected();

Check out this JSfiddle and open the console for logged results.http://jsfiddle.net/S6LzP/

More granular, Looking at the defer function on line 488:

function defer() {    // if "messages" is an "Array", that indicates that the promise has not yet    // been resolved.  If it is "undefined", it has been resolved.  Each    // element of the messages array is itself an array of complete arguments to    // forward to the resolved promise.  We coerce the resolution value to a    // promise using the `resolve` function because it handles both fully    // non-thenable values and other thenables gracefully.    var messages = [], progressListeners = [], resolvedPromise;    var deferred = object_create(defer.prototype);    var promise = object_create(Promise.prototype);    promise.promiseDispatch = function (resolve, op, operands) {        var args = array_slice(arguments);        if (messages) {            messages.push(args);            if (op === "when" && operands[1]) { // progress operand                progressListeners.push(operands[1]);            }        } else {            nextTick(function () {                resolvedPromise.promiseDispatch.apply(resolvedPromise, args);            });        }    };    // XXX deprecated    promise.valueOf = function () {        if (messages) {            return promise;        }        var nearerValue = nearer(resolvedPromise);        if (isPromise(nearerValue)) {            resolvedPromise = nearerValue; // shorten chain        }        return nearerValue;    };    promise.inspect = function () {        if (!resolvedPromise) {            return { state: "pending" };        }        return resolvedPromise.inspect();    };    if (Q.longStackSupport && hasStacks) {        try {            throw new Error();        } catch (e) {            // NOTE: don't try to use `Error.captureStackTrace` or transfer the            // accessor around; that causes memory leaks as per GH-111. Just            // reify the stack trace as a string ASAP.            //            // At the same time, cut off the first line; it's always just            // "[object Promise]\n", as per the `toString`.            promise.stack = e.stack.substring(e.stack.indexOf("\n") + 1);        }    }    // NOTE: we do the checks for `resolvedPromise` in each method, instead of    // consolidating them into `become`, since otherwise we'd create new    // promises with the lines `become(whatever(value))`. See e.g. GH-252.    function become(newPromise) {        resolvedPromise = newPromise;        promise.source = newPromise;        array_reduce(messages, function (undefined, message) {            nextTick(function () {                newPromise.promiseDispatch.apply(newPromise, message);            });        }, void 0);        messages = void 0;        progressListeners = void 0;    }    deferred.promise = promise;    deferred.resolve = function (value) {        if (resolvedPromise) {            return;        }        become(Q(value));    };    deferred.fulfill = function (value) {        if (resolvedPromise) {            return;        }        become(fulfill(value));    };    deferred.reject = function (reason) {        if (resolvedPromise) {            return;        }        become(reject(reason));    };    deferred.notify = function (progress) {        if (resolvedPromise) {            return;        }        array_reduce(progressListeners, function (undefined, progressListener) {            nextTick(function () {                progressListener(progress);            });        }, void 0);    };    return deferred;}

Mostly notably the method at the very bottom deferred.notify.

Example usage:

function requestOkText(url) {    var request = new XMLHttpRequest();    var deferred = Q.defer();    request.open("GET", url, true);    request.onload = onload;    request.onerror = onerror;    request.onprogress = onprogress;    request.send();    function onload() {        if (request.status === 200) {            deferred.resolve(request.responseText);        } else {            deferred.reject(new Error("Status code was " + request.status));        }    }    function onerror() {        deferred.reject(new Error("Can't XHR " + JSON.stringify(url)));    }    function onprogress(event) {        deferred.notify(event.loaded / event.total);    }    return deferred.promise;}requestOkText("http://localhost:3000").then(function (responseText) {    // If the HTTP response returns 200 OK, log the response text.    console.log(responseText);}, function (error) {    // If there's an error or a non-200 status code, log the error.    console.error(error);}, function (progress) {    // Log the progress as it comes in.    console.log("Request progress: " + Math.round(progress * 100) + "%");});