【问题标题】:Get state of Angular deferred?获取 Angular 的延迟状态?
【发布时间】:2023-03-21 16:58:01
【问题描述】:

使用 jQuery deferreds 我习惯于像这样检查当前状态:

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

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

【问题讨论】:

    标签: jquery angularjs promise angular-promise


    【解决方案1】:

    更新

    由于 $q 的重构,这现在是可能的,尽管没有记录:

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

    原创

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

    没有办法从外部实现这一点。

    您必须在 Promise 上调用 .then,并且该处理程序中的代码将在 Promise 实现时运行。

    【讨论】:

    • 它看起来像是被添加到 1.3.0 - github.com/angular/angular.js/blob/v1.3.0/src/ng/q.js。在此之前我找不到任何对 $$state 的引用。
    • status 也可以是-1,但我不确定它意味着什么? github.com/angular/angular.js/blob/v1.4.3/src/ng/q.js#L365
    • 以“$$”开头的属性应该被视为私有API,而不是在您自己的代码中使用。但是,我认为没有公开的 API 用于公开 Angular 承诺的状态。
    • @muttonUp: -1 似乎是第二个已解决的情况,如 1。它用于表示已解决的值何时是一个函数,或者该值是否需要沿承诺链传递。我想 -1 的意思是“到目前为止还不错,但在解决方案完成之前我们还有更多代码要运行”。
    【解决方案2】:

    您的问题的答案是:是的,有办法。其他答案很好地涵盖了 $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-instantiated deferredpromise 对象都将有一个state 属性值为待处理、已完成已拒绝

    查看this plunk


    怀疑?

    你正在有效地修改 $q 本身,用另一个 deferred 包装每个 deferred

    实际上并非如此。 $q 的原始defer() 构造函数只被调用一次。它通过then 在内部附加一个事件处理程序,简单地装饰了附加功能。 [请注意,附加 then 回调的结果会实例化一个附加 defer 对象,该回调是随每个 deferred 对象自动创建的...这是意料之中的,因为这就是 angular 的工作方式内部。]

    这行不通,因为不应使用 deferred 创建 Promise,而是将其与从 API 返回的 Promise 链接起来

    请注意,此代码将装饰由 $q 服务创建的 每个 延迟(以及 promise 对象)。这意味着任何使用 $q 的 API 都将自动使用 state 属性进行修饰。所以不管你如何使用$q,无论是使用一些API还是它自己,这个解决方案都装饰了deferred对象和promise,我提供了the plunk来证明这一点。


    有生产价值吗?

    这种方法可单元测试保证不会破坏任何已经使用$q的应用程序,并且灵活您可以稍后在 $q 中添加额外的装饰器,而无需修改旧的。

    【讨论】:

    • 是的,这是行不通的,因为不应使用 deferred 创建 Promise,而是将其与从 API 返回的 Promise 链接起来
    • 我已经在回答中回复了你的 cmets。
    • @NateBarbettini 很好 - 一方面,$q 在 1.2 的生命周期中经历了几次重大变化,在 1.2 和 1.3 之间进行了一次巨大的内部检修,并且几乎经历了更大的检修。今天你将完全不同(延迟和承诺是基于原型的) - 你可以直接覆盖 .then 而无需此解决方案的开销(创建开销并为每个通过 Angular 的承诺分配额外的闭包 i>) Promise 在 Angular 中很慢(最近变得更好)。
    • 我很想知道 Promise 的用例,其中实例化闭包和每个 Promise 的一些简单对象会产生任何可察觉的性能差异。
    • 这在 Angular 1.6.4 中并不一致。这只会修饰通过$q.defer 做出的承诺,并不是每个承诺都是从该方法生成的。具体来说,rejectwhen/resolveall 方法在内部专门调用 new Promise(),而 race 调用 defer。处理这个问题的最好方法是装饰 Promise 的构造函数,但整个 API 是内部的。
    【解决方案3】:

    更新:

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

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

    其他:

    这是针对 Q 库的,不是 angular 的 $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) + "%");
    });
    

    【讨论】:

    【解决方案4】:

    受 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;
            }]);
        }
    })();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      • 1970-01-01
      • 1970-01-01
      • 2018-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多