获取Angular状态推迟?

使用jQuery延迟,我习惯于能够像这样检查当前状态:

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

有没有办法做同样的Angular延期? (甚至更好的承诺)

更新

由于$ q的重构,现在虽然没有logging,但现在是可能的:

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

原文

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

从外部无法实现这一点。

你必须打电话给承诺,那个.then的代码将在承诺履行时运行。

你的问题的答案是: 是的,有一种方法 。 其他答案很好地涵盖了$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 延迟承诺对象将有一个state属性值, 挂起履行 ,或拒绝

看看这个plunk


持怀疑态度?

你正在有效地修改$ q本身,将每个延期与另一个延期包装在一起

事实上并非如此。 $q原来的defer()构造函数被调用一次。 它通过内部附加一个事件处理程序简单地装饰附加function。 [请注意,由于附加的callback函数实例化了一个额外的defer对象,这个callback函数是每个延期的对象自动创build的…这是预期的,因为这是angular度如何工作的。]

这是行不通的,因为承诺不应该延迟创build,而是从apis返回的承诺链接

请注意,此代码将修饰由$q服务创build的每个延迟(并因此promise对象)。 这意味着任何利用$ q的API都将自动用state属性进行修饰 。 所以无论你如何使用$q ,无论是使用某个API还是使用它自己,这个解决scheme都装饰了deferred对象和promise ,而且我提供了这个plunk来certificate它。


生产价值?

这种方法是unit testing的 ,它保证不会破坏任何已经使用$q应用程序 ,而且它的灵活性在于,您可以稍后将其他修饰器添加到$q而无需修改旧的修饰器。

更新:

不幸的是,这看起来不像$q 。 你将不得不把这个代码放在你的方法。

 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.inspect().state ['pending', 'rejected', 'fulfilled']

你也有:

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

看看这JSfiddle并打开控制台的logging结果。 http://jsfiddle.net/S6LzP/

更细化,查看488行的deferfunction:

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

我提出了一个由Gil和Travis的答案启发的解决scheme,它使用更接近Q实现的方法来装饰Promise构造函数。

请注意,这个装饰依赖于Promise.$$state 。 这是为Angular 1.6.4构build的,理论上应该一直工作到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; }]); } })();