jQuery 获取延迟的 Angular 状态?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24091513/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 02:27:23  来源:igfitidea点击:

Get state of Angular deferred?

jqueryangularjspromiseangular-promise

提问by Evan Hobbs

With jQuery deferreds I'm used to be able to check the current state like this:

使用 jQuery deferreds,我可以像这样检查当前状态:

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

Is there a way to do the same for Angular deferreds? (or even better promises)

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

回答by Benjamin Gruenbaum

Update:

更新

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

由于 $q 的重构,虽然没有记录,但现在可以实现:

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

Original:

原文

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

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

There is no way to achieve this from the outside.

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

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

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

回答by Gil Birman

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 $qusing the $provideservice's decorator function.

你的问题的答案是:是的,有办法。其他答案很好地涵盖了$q. 然而,这是很容易的状态属性添加到$q使用$provide服务的装饰功能

  $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 configblock, and all $q-instantiated deferredand promiseobjects will have a stateproperty with the value pending, fulfilled, or rejected.

把这个内部装饰config块和所有$q-instantiated推迟,并承诺对象将有一个state与值属性待定履行拒绝

Check out this plunk

看看这个笨蛋



Skeptical?

持怀疑态度?

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

您正在有效地修改 $q 本身,用另一个延迟包装每个延迟

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 deferobject is instantiated as a result of the additional thencallback which is automatically created with each deferredobject... which is to be expected because this is how angular works internally.]

事实上,情况并非如此。$q的原始defer()构造函数只被调用一次。通过在内部附加一个事件处理程序,它只是用附加功能装饰then。[请注意,defer由于then每个延迟对象自动创建的附加回调,附加对象被实例化......这是可以预料的,因为这是 angular 在内部工作的方式。]

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

这是行不通的,因为不应该使用 deferred 创建 promise,而是从 apis 返回的 promise 链接起来

Note that this code will decorate everydeferred (and thus promiseobject) which is created by the $qservice. This means that any API which utilizes $q will be automatically decorated with the stateproperty. So regardless of how you use $q, whether with some API or on it's own, this solution decorates both the deferredobject and the promise, and I have provided the plunkto prove it.

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



Production-worthy?

值得生产吗?

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

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

回答by Travis

Updated:

更新:

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

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

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 $qbut similar.

这是针对 Q 库的,不是 angular$q而是类似的。

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

Angular 的灵感来自于Q库,查看源代码,它实际上并没有那么可怕。https://github.com/kriskowal/q/blob/v1/q.js

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

你可以使用myPromise.inspect().state['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/

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

More granular, Looking at the deferfunction on line 488:

更细化,看defer第 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.

最值得注意的是最底层的方法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) + "%");
});

回答by p0lar_bear

I whipped up a solution inspired by Gil and Travis's answers, which decorates the Promise constructor with methods closer to the Q implementation.

我根据 Gil 和 Travis 的回答提出了一个解决方案,它用更接近 Q 实现的方法装饰 Promise 构造函数。

Note that this decoration relies on Promise.$$state. This was built for Angular 1.6.4, and theoretically should work all the way down to 1.3.x, but no guarantees on that or future releases:

请注意,此装饰依赖于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;
        }]);
    }
})();