Avec jQuery différé, je suis habitué à pouvoir vérifier l'état actuel de la manière suivante:
var defer = $.Deferred();
defer.state(); //Returns the state of the deferred, eg 'resolved'
Existe-t-il un moyen de faire de même pour les reports angulaires? (ou même mieux promesses)
Mettre à jour :
En raison de la refactorisation de $ q, ceci est maintenant possible bien que non documenté:
promise.$$state.status === 0 // pending
promise.$$state.status === 1 // resolved
promise.$$state.status === 2 // rejected
Original :
Contrairement à la plupart des bibliothèques de promesses (Bluebird, Q, when, RSVP, etc.), $ q n'expose pas une API d'inspection synchrone.
Il n'y a aucun moyen d'y parvenir de l'extérieur.
Vous devez appeler .then
sur la promesse et le code dans ce gestionnaire s'exécutera lorsque la promesse sera remplie.
La réponse à votre question est la suivante: oui, il existe un moyen. Les autres réponses couvrent bien les limitations intégrées de $q
. Cependant, il est facile d'ajouter une propriété state à $q
en utilisant the $provide
service, la fonction de décorateur.
$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;
});
Placez ce décorateur dans un bloc config
et tous les objets $q
- instanciés différé et promis auront une propriété state
avec la valeur en attente, rempli ou rejeté.
_ {vous modifiez effectivement $ q lui-même, en encapsulant chaque différé avec un autre différé}
En réalité ce n'est pas le cas. Le constructeur defer()
original de $q
est appelé exactement une fois. Il est simplement décoré avec des fonctionnalités supplémentaires en attachant en interne un gestionnaire d’événements via then
. [Notez qu'un objet defer
supplémentaire est instancié à la suite du rappel then
supplémentaire qui est automatiquement créé avec chaque objet différé ... ce à quoi il faut s'attendre, car c'est ainsi que angular fonctionne en interne.]
_ {cela ne fonctionnerait pas car les promesses ne devraient pas être créées avec différé, mais enchaînées à partir des promesses retournées d'apis}
Notez que ce code va décorer chaque différé (et donc l'objet promise
) créé par le service $q
. Cela signifie que toute API utilisant $ q sera automatiquement décorée avec la propriété state
. Ainsi, quelle que soit la façon dont vous utilisez $q
, que ce soit avec une API ou par elle-même, cette solution décore à la fois l'objet deferred
et la promise
et j'ai fourni le plunk pour le prouver.
Cette approche est unit testable, est garanti de ne pas interrompre une application utilisant déjà $q
, et c’est flexible dans le sens où vous pourrez ajouter ultérieurement des décorateurs à $q
sans modifier l’ancien ( s).
Malheureusement, cela ne semble pas possible avec $q
. Vous devrez mettre ce code dans votre méthode then
.
myPromise()
.then(function() {
// everything in here resolved
},
function() {
// everything in here rejected
},
function() {
// everything in here pending (with progress back)
});
Ceci est pour la bibliothèque Q pas $q
de angular, mais similaire.
Angular est inspiré de la bibliothèque Q
, consultez le source, ce n’est pas si effrayant que cela. https://github.com/kriskowal/q/blob/v1/q.js
Vous pouvez utiliser myPromise.inspect().state
il y a ['pending', 'rejected', 'fulfilled']
Vous avez aussi:
myPromise.isFulfilled();
myPromise.isPending();
myPromise.isRejected();
Découvrez ce JSfiddle et ouvrez la console pour les résultats enregistrés . http://jsfiddle.net/S6LzP/
Plus granulaire, En regardant la fonction defer
à la ligne 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;
}
Surtout la méthode tout en bas deferred.notify
.
Exemple d'utilisation:
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) + "%");
});
J'ai élaboré une solution inspirée des réponses de Gil et Travis, qui décore le constructeur de Promise avec des méthodes plus proches de l'implémentation de Q.
Notez que cette décoration s'appuie sur Promise.$$state
. Cela a été construit pour Angular 1.6.4, et devrait théoriquement fonctionner jusqu’à la version 1.3.x, mais aucune garantie sur cette version ou les versions futures:
(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;
}]);
}
})();