Mettre à jour:
Pour aider les futurs téléspectateurs de cet article, j'ai créé cette démo de la réponse de pluma .
Question:
Mon objectif semble assez simple.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
Le problème ici est que si j'échoue à l'étape 1, les deux stepError(1)
AND stepError(2)
sont déclenchés. Si je ne return $q.reject
puis stepError(2)
ne sera pas tiré, mais step(2)
sera, ce que je comprends. J'ai tout accompli sauf ce que j'essaye de faire.
Comment écrire des promesses pour pouvoir appeler une fonction en cas de rejet, sans appeler toutes les fonctions de la chaîne d'erreur? Ou y a-t-il une autre façon d'accomplir cela?
Voici une démo en direct pour que vous puissiez travailler avec quelque chose.
Mettre à jour:
J'ai en quelque sorte résolu le problème. Ici, j'attrape l'erreur à la fin de la chaîne et reject(data)
je transmets les données à afin de savoir quel problème gérer dans la fonction d'erreur. Cela ne répond pas à mes besoins car je ne veux pas dépendre des données. Ce serait nul, mais dans mon cas, il serait plus propre de transmettre un rappel d'erreur à la fonction plutôt que de dépendre des données renvoyées pour déterminer ce qu'il faut faire.
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Promise.prototype.catch()
Les exemples sur MDN montrent la solution pour les mêmes problèmes.Réponses:
La raison pour laquelle votre code ne fonctionne pas comme prévu est qu'il fait en fait quelque chose de différent de ce que vous pensez qu'il fait.
Disons que vous avez quelque chose comme ce qui suit:
Pour mieux comprendre ce qui se passe, supposons qu'il s'agit d'un code synchrone avec
try
/catch
blocks:Le
onRejected
gestionnaire (le deuxième argument dethen
) est essentiellement un mécanisme de correction d'erreur (comme uncatch
bloc). Si une erreur est renvoyéehandleErrorOne
, elle sera interceptée par le prochain bloc catch (catch(e2)
), et ainsi de suite.Ce n'est évidemment pas ce que vous vouliez.
Disons que nous voulons que toute la chaîne de résolution échoue, peu importe ce qui ne va pas:
Remarque: nous pouvons laisser le
handleErrorOne
où il est, car il ne sera invoqué qu'en cas destepOne
rejet (c'est la première fonction de la chaîne, donc nous savons que si la chaîne est rejetée à ce stade, cela ne peut être qu'à cause de la promesse de cette fonction) .Le changement important est que les gestionnaires d'erreurs pour les autres fonctions ne font pas partie de la chaîne de promesse principale. Au lieu de cela, chaque étape a sa propre «sous-chaîne» avec une
onRejected
qui n'est appelée que si l'étape a été rejetée (mais ne peut pas être atteinte directement par la chaîne principale).La raison pour laquelle cela fonctionne est que les deux
onFulfilled
etonRejected
sont des arguments facultatifs de lathen
méthode. Si une promesse est remplie (c'est-à-dire résolue) et que le suivantthen
dans la chaîne n'a pas deonFulfilled
gestionnaire, la chaîne continuera jusqu'à ce qu'il y en ait un avec un tel gestionnaire.Cela signifie que les deux lignes suivantes sont équivalentes:
Mais la ligne suivante n'est pas équivalente aux deux ci-dessus:
La bibliothèque de promesses d'Angular
$q
est basée sur laQ
bibliothèque de kriskowal (qui a une API plus riche, mais contient tout ce que vous pouvez trouver$q
). La documentation de l'API de Q sur GitHub pourrait s'avérer utile. Q implémente la spécification Promises / A + , qui explique en détail commentthen
et le comportement de résolution des promesses fonctionne exactement.ÉDITER:
Gardez également à l'esprit que si vous souhaitez sortir de la chaîne dans votre gestionnaire d'erreurs, il doit renvoyer une promesse rejetée ou lancer une erreur (qui sera automatiquement interceptée et encapsulée dans une promesse rejetée). Si vous ne renvoyez pas de promesse,
then
encapsule la valeur de retour dans une promesse de résolution pour vous.Cela signifie que si vous ne retournez rien, vous renvoyez effectivement une promesse résolue pour la valeur
undefined
.la source
if you don't return anything, you are effectively returning a resolved promise for the value undefined.
Merci @plumastepOne().then(stepTwo, handleErrorOne)
`stepOne (). then (null, handleErrorOne) .then (stepTwo)` Sont-ils vraiment équivalents? Je pense qu'en cas de rejet,stepOne
la deuxième ligne de code s'exécuterastepTwo
mais la première ne s'exécuterahandleErrorOne
et s'arrêtera. Ou est-ce que je manque quelque chose?Un peu tard à la fête mais cette solution simple a fonctionné pour moi:
Cela vous permet de casser de la chaîne.
la source
.then(user => { if (user) return Promise.reject('The email address already exists.') })
.then(user => { if (user) throw 'The email address already exists.' })
Vous avez besoin d'une
.then()
chaîne répétitive avec un cas spécial pour commencer et un cas spécial pour terminer.Le truc est de faire passer le numéro de l'étape du cas d'échec à un gestionnaire d'erreur final.
step(1)
sans condition..then()
avec les rappels suivants:.then()
sans gestionnaire de succès et un gestionnaire d'erreur final.Vous pouvez écrire le tout à la main, mais il est plus facile de démontrer le modèle avec des fonctions nommées et généralisées:
voir la démo
Notez comment dans
step()
, le différé est rejeté ou résolu avecn
, rendant ainsi cette valeur disponible pour les rappels du suivant.then()
dans la chaîne. Une foisstepError
appelée, l'erreur est renvoyée à plusieurs reprises jusqu'à ce qu'elle soit traitée parfinalError
.la source
Lors du rejet, vous devez transmettre une erreur de rejet, puis envelopper les gestionnaires d’erreurs d’étape dans une fonction qui vérifie si le rejet doit être traité ou «relancé» jusqu’à la fin de la chaîne:
Ce que vous verriez sur la console:
Voici un code de travail https://jsfiddle.net/8hzg5s7m/3/
Si vous avez une gestion spécifique pour chaque étape, votre wrapper pourrait être quelque chose comme:
puis ta chaîne
la source
Si je comprends bien, vous ne voulez que l'erreur de l'étape qui a échoué, non?
Cela devrait être aussi simple que de changer le cas d'échec de la première promesse en ceci:
En retournant
$q.reject()
dans le cas d'échec de la première étape, vous rejetez cette promesse, ce qui provoque l'appel de errorCallback dans la 2èmethen(...)
.la source
step(2)
. Maintenant, je viens de réessayer, cela ne se produit pas. Je suis tellement confus.return step(2);
ne doit être appelée que lorsqu'elle eststep(1)
résolue avec succès.return $q.reject()
, la chaîne va continuer. Dans ce cas, toutreturn response
gâché. Voir ceci: jsbin.com/EpaZIsIp/6/edithttp://jsbin.com/EpaZIsIp/20/edit
Ou automatisé pour n'importe quel nombre d'étapes:
http://jsbin.com/EpaZIsIp/21/edit
la source
deferred.reject(n)
je reçois un avertissement que la promesse est rejetée avec un objetEssayez d'utiliser ceci comme libs:
https://www.npmjs.com/package/promise-chain-break
la source
Si vous souhaitez résoudre ce problème en utilisant async / await:
la source
Attachez les gestionnaires d'erreurs en tant qu'éléments de chaîne séparés directement à l'exécution des étapes:
ou en utilisant
catch()
:Remarque: Il s'agit essentiellement du même schéma que celui suggéré par Pluma dans sa réponse, mais en utilisant la dénomination du PO.
la source
Trouvé des
Promise.prototype.catch()
exemples sur MDN ci-dessous très utiles.(La réponse acceptée mentionne
then(null, onErrorHandler)
ce qui est fondamentalement la même quecatch(onErrorHandler)
.)la source
La meilleure solution est de refactoriser votre chaîne de promesses pour utiliser ES6 await. Ensuite, vous pouvez simplement revenir de la fonction pour ignorer le reste du comportement.
Je me suis cogné la tête contre ce modèle depuis plus d'un an et utiliser Wait's est le paradis.
la source
Utiliser un module SequentialPromise
Intention
Fournir un module dont la responsabilité est d'exécuter les requêtes de manière séquentielle, tout en suivant l'index actuel de chaque opération de manière ordinale. Définissez l'opération dans un modèle de commande pour plus de flexibilité.
Les participants
execute
méthode pour enchaîner et suivre chaque opération. SequentialPromise renvoie une Promise-Chain à partir de toutes les opérations effectuées.execute
méthode tout en passant une liste ordinale d'options pour chaque opération.Conséquences
Utilisez SequentialPromise lorsque le comportement ordinal de la résolution Promise est nécessaire. SequentialPromise suivra l'index pour lequel une promesse a été rejetée.
la mise en oeuvre
Essentiel
SéquentiellePromise
la source
Si à tout moment vous revenez,
Promise.reject('something')
vous serez jeté dans le bloc catch à la promesse.Si la première promesse ne renvoie aucun résultat, vous n'obtiendrez que «Aucun résultat» dans la console.
la source