Je fais une telle promesse,
function getMode(){
var deferred = Promise.defer();
checkIf('A')
.then(function(bool){
if(bool){
deferred.resolve('A');
}else{
return checkIf('B');
}
}).then(function(bool){
if(bool){
deferred.resolve('B');
}else{
return checkIf('C');
}
}).then(function(bool){
if(bool){
deferred.resolve('C');
}else{
deferred.reject();
}
});
return deferred.promise;
}
checkIf
renvoie une promesse, et oui checkIf
ne peut pas être modifié .
Comment sortir de la chaîne lors du premier match? (de toute autre manière que de jeter explicitement une erreur?)
Je pense que vous ne voulez pas d'une chaîne ici. De manière synchrone, vous auriez écrit
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
et voici comment cela devrait être traduit en promesses:
function getMode(){
checkIf('A').then(function(bool) {
if (bool)
return 'A';
return checkIf('B').then(function(bool) {
if (bool)
return 'B';
return checkIf('C').then(function(bool) {
if (bool)
return 'C';
throw new Error();
});
});
});
}
Il n'y a pas de if else
- aplatissement des promesses.
Toute autre manière que de jeter explicitement une erreur?
Vous devrez peut-être jeter quelque chose, mais cela ne doit pas nécessairement être une erreur.
La plupart des implémentations de promesse ont la méthode catch
acceptant le premier argument comme type d'erreur (mais pas toutes, et pas la promesse ES6).
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
J'ajoute la possibilité de casser la mise en œuvre récente de ma propre bibliothèque de promesses. Et si vous utilisiez ThenFail (comme vous ne le feriez probablement pas), vous pouvez écrire quelque chose comme ceci:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
Je voudrais juste utiliser coroutines/spawns , cela conduit à beaucoup code simple
function* getMode(){
if(yield checkIf('A'))
return 'A';
if(yield checkIf('B'))
return 'B';
if(yield checkIf('C'))
return 'C';
throw undefined; // don't actually throw or reject with non `Error`s in production
}
Si vous n'avez pas de générateurs, il y a toujours traceur ou 6to5.
Vous pouvez utiliser return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
L'instruction return retourne un "then-capable", seulement que la méthode then ne fait rien . Lorsqu'elle est renvoyée d'une fonction dans then (), then () essaiera d'obtenir le résultat du thenable . then -able's "then" prend un rappel mais ne sera jamais appelé dans ce cas. Donc "then ()" retourne et le rappel pour le reste de la chaîne ne se produit pas.
Vous pouvez créer une fonction firstSucceeding
qui renverrait la valeur de la première opération réussie ou jettera une NonSucceedingError
.
J'ai utilisé les promesses ES6, mais vous pouvez adapter l'algorithme pour prendre en charge l'interface de promesse de votre choix.
function checkIf(val) {
console.log('checkIf called with', val);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, [val, val === 'B']), 0);
});
}
var firstSucceeding = (function () {
return function (alternatives, succeeded) {
var failedPromise = Promise.reject(NoneSucceededError());
return (alternatives || []).reduce(function (promise, alternative) {
return promise.then(function (result) {
if (succeeded(result)) return result;
else return alternative();
}, alternative);
}, failedPromise).then(function (result) {
if (!succeeded(result)) throw NoneSucceededError();
return result;
});
}
function NoneSucceededError() {
var error = new Error('None succeeded');
error.name = 'NoneSucceededError';
return error;
}
})();
function getMode() {
return firstSucceeding([
checkIf.bind(null, 'A'),
checkIf.bind(null, 'B'),
checkIf.bind(null, 'C')
], function (result) {
return result[1] === true;
});
}
getMode().then(function (result) {
console.log('res', result);
}, function (err) { console.log('err', err); });
J'arrive probablement tard dans la soirée, mais j'ai récemment posté une réponse à l'aide de générateurs et de la bibliothèque co
qui répondrait à cette question (voir la solution 2):
Le code serait quelque chose comme:
const requestHandler = function*() {
const survey = yield Survey.findOne({
_id: "bananasId"
});
if (survey !== null) {
console.log("use HTTP PUT instead!");
return;
}
try {
//saving empty object for demonstration purposes
yield(new Survey({}).save());
console.log("Saved Successfully !");
return;
}
catch (error) {
console.log(`Failed to save with error: ${error}`);
return;
}
};
co(requestHandler)
.then(() => {
console.log("finished!");
})
.catch(console.log);
Vous écririez à peu près du code synchrone qui serait en réalité asynchrone!
J'espère que ça aide!
j'aime beaucoup de réponses postées jusqu'à présent qui atténuent ce que le q readme appelle la "pyramide de Doom". dans l'intérêt de la discussion, j'ajouterai le motif que j'ai expliqué avant de chercher pour voir ce que font les autres. j'ai écrit une fonction comme
var null_wrap = function (fn) {
return function () {
var i;
for (i = 0; i < arguments.length; i += 1) {
if (arguments[i] === null) {
return null;
}
}
return fn.apply(null, arguments);
};
};
et j’ai fait quelque chose de tout à fait analogue à la réponse de @vilicvane, sauf que plutôt que throw new BreakSignal()
, j’avais écrit return null
et encapsulé tous les rappels .then
suivants dans null_wrap
comme
then(null_wrap(function (res) { /* do things */ }))
je pense que c'est une bonne réponse car cela évite beaucoup d'indentation et que l'OP a spécifiquement demandé une solution qui ne soit pas throw
. Cela dit, je peux revenir en arrière et utiliser quelque chose qui ressemble davantage à ce que @vilicvane a fait car certaines promesses de bibliothèques pourraient renvoyer null
pour indiquer autre chose que "casser la chaîne", ce qui pourrait prêter à confusion.
il s’agit plus d’un appel à plus de commentaires/réponses que d’une réponse "c’est vraiment la façon de le faire".