获得Angular的状态延期?

时间:2014-06-06 22:24:20

标签: jquery angularjs promise angular-promise

随着jQuery延迟,我以前能够像这样检查当前状态:

var defer = $.Deferred();
defer.state();  //Returns the state of the deferred, eg 'resolved'

Angular deferreds有没有办法做同样的事情? (甚至更好的承诺)

4 个答案:

答案 0 :(得分:84)

<强>更新

由于$ q的重构,现在可以实现,尽管没有记录:

promise.$$state.status === 0 // pending
promise.$$state.status === 1 // resolved
promise.$$state.status === 2 // rejected

<强>原始

与大多数promise库(Bluebird,Q,when,RSVP等)不同,$ q不公开同步检查API。

没有办法从外面实现这一目标。

你必须在承诺上调用.then,并且当承诺履行时,该处理程序中的代码将运行。

答案 1 :(得分:34)

您的问题的答案是:是的,有办法。其他答案很好地涵盖了 $q 的内置限制。但是,使用 $provide服务的装饰函数将状态属性添加到 $q 很容易。

  $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;
  });

将此装饰器放在config块内,所有$q - 实例化延迟 promise 对象将具有{{1}值为待处理已履行已拒绝的属性。

查看this plunk


怀疑?

  

你正在有效地修改$ q本身,将每个延迟包装为另一个延迟的

实际情况并非如此。 state 的原始$q构造函数只调用一次。它通过defer()内部附加事件处理程序,通过附加功能进行简单修饰。 [请注意,额外的then对象会因为每个延迟对象自动创建的额外defer回调而被实例化...这是预期的,因为这是角度如何在内部工作。]

  

这不会起作用,因为承诺不应该是延期创建的,而是从apis返回的承诺中链接

请注意,此代码将修饰由 then 服务创建的每个延迟(以及promise对象)。这意味着任何使用$ q的API都将使用$q属性自动修饰。因此无论您使用state的方式如何,无论是使用某些API还是使用它,此解决方案都会对$q对象和deferred进行修饰,并且我提供了{{} 3}}证明它。


值得生产的?

此方法单元可测试保证不会破坏已使用promise 的任何应用程序,并且它&#39;灵活的,因为您以后可以在$q添加额外的装饰器而不修改旧的装饰器。

答案 2 :(得分:1)

更新

不幸的是,$q看起来不太可能。您必须将此代码放在then方法中。

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

其他:

这是针对Q库而不是角度$q但是类似的。

Angular受Q库的启发,查看源代码,实际上并不那么可怕。 https://github.com/kriskowal/q/blob/v1/q.js

您可以myPromise.inspect().state使用['pending', 'rejected', 'fulfilled']

您还有:

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

查看此JSfiddle并打开控制台以记录结果。 http://jsfiddle.net/S6LzP/

更精细,查看第488行的defer函数:

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;
}

主要是最底层deferred.notify的方法。

使用示例:

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) + "%");
});

答案 3 :(得分:0)

我提出了一个灵感来自Gil和Travis的答案的解决方案,它使用更接近Q实现的方法来修饰Promise构造函数。

请注意,此装饰依赖于Promise.$$state。这是为Angular 1.6.4构建的,理论上应该一直工作到1.3.x,但在该版本或未来版本中没有保证:

(function() {
    'use strict';

    angular
        .module('your.module.name.goes.here')
        .config(configBlock);

    /** @ngInject */
    configBlock.$inject = ['$provide'];
    function configBlock($provide) {
        $provide.decorator('$q', ['$delegate', function ($delegate) {
            console.log($delegate);
            var Promise = $delegate.prototype.constructor;

            Promise.prototype.inspect = function () {
                var inspect = {};
                switch (this.$$state.status) {
                    case -1:
                    case 0:
                        inspect.state = 'pending';
                        break;
                    case 1:
                        inspect.state = 'fulfilled';
                        break;
                    case 2:
                        inspect.state = 'rejected';
                        break;
                    default:
                        inpsect.state = 'unknown';
                }
                return inspect;
            };

            Promise.prototype.isFulfilled = function () {
                return this.inspect().state === 'fulfilled';
            }
            Promise.isFulfilled = function (obj) {
                if (obj.constructor !== Promise) {
                    return true;
                }
                return obj.isFulfilled();
            }

            Promise.prototype.isRejected = function () {
                return this.inspect().state === 'rejected';
            }
            Promise.isRejected = function (obj) {
                if (obj.constructor !== Promise) {
                    return false;
                }
                return obj.isRejected();
            }

            Promise.prototype.isPending = function () {
                return this.inspect().state === 'pending';
            }
            Promise.isPending = function (obj) {
                if (obj.constructor !== Promise) {
                    return false;
                }
                return obj.isPending();
            }

            return $delegate;
        }]);
    }
})();