Как сломать цепочку обещаний
Я обещаю таким образом,
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
возвращает обещание, и да checkIf
не может быть изменено.
Как я вырваться из цепочки в первом матче? (каким-либо образом, кроме явно бросая ошибку?)
Ответы
Ответ 1
Я думаю, что вы не хотите здесь цепочки. Синхронно, вы бы написали
function getMode(){
if (checkIf('A')) {
return 'A';
} else {
if (checkIf('B')) {
return 'B';
} else {
if (checkIf('C')) {
return 'C';
} else {
throw new Error();
}
}
}
}
и вот как это должно быть переведено на promises:
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();
});
});
});
}
В promises нет if else
-flattening.
Ответ 2
В любом случае, кроме явного отклонения ошибки?
Возможно, вам нужно что-то бросить, но это не должно быть ошибкой.
Большинство реализаций обещаний имеют метод catch
, принимающий первый аргумент как тип ошибки (но не все, а не обещание ES6), было бы полезно в этой ситуации:
function BreakSignal() { }
getPromise()
.then(function () {
throw new BreakSignal();
})
.then(function () {
// Something to skip.
})
.catch(BreakSignal, function () { })
.then(function () {
// Continue with other works.
});
Я добавляю способность ломаться в недавней реализации моей собственной библиотеки обещаний. И если вы использовали ThenFail (как вы, вероятно, не знаете), вы можете написать примерно так:
getPromise()
.then(function () {
Promise.break;
})
.then(function () {
// Something to skip.
})
.enclose()
.then(function () {
// Continue with other works.
});
Ответ 3
Я бы просто использовал сопрограммы/нересты, это приводит к значительному упрощению кода:
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
}
Если у вас нет генераторов, тогда всегда отслеживается или 6to5.
Ответ 4
Вы можете использовать
return { then: function() {} };
.then(function(bool){
if(bool){
deferred.resolve('A');
return { then: function() {} }; // end/break the chain
}else{
return checkIf('B');
}
})
Оператор return возвращает "then-able", только тогда метод then ничего не делает.
При возврате из функции в then(), then() попытается получить результат от thenable.
Тогда-то "затем" принимает обратный вызов, но в этом случае он никогда не будет вызван. Таким образом, "then()" возвращается, а обратный вызов для остальной части цепочки не выполняется.
Ответ 5
Вы можете создать функцию firstSucceeding
, которая либо вернет значение первой успешной операции, либо выбросит NonSucceedingError
.
Я использовал ES6 promises, но вы можете адаптировать алгоритм для поддержки интерфейса обещаний по вашему выбору.
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); });
Ответ 6
Мне нравится много ответов, опубликованных до сих пор, которые смягчают то, что q readme вызывает "пирамиду обречения". для обсуждения, я добавлю шаблон, который я выложил, прежде чем искать, чтобы узнать, что делают другие люди. я написал такую функцию, как
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);
};
};
и я сделал что-то совершенно аналогичное ответу @vilicvane, кроме, чем throw new BreakSignal()
, я написал return null
и завернул все последующие обратные вызовы .then
в null_wrap
, например
then(null_wrap(function (res) { /* do things */ }))
Я думаю, что это хороший ответ b/c, он избегает множества отступов и b/c OP специально запрашивает решение, которое не throw
. что я могу вернуться и использовать что-то более похожее на то, что @vilicvane сделал b/c, некоторая библиотека promises могла бы вернуть null
, чтобы указать что-то другое, кроме "разбить цепочку", и это может сбить с толку.
это скорее призыв к большему количеству комментариев/ответов, чем ответ "это определенно способ сделать это".
Ответ 7
Вероятно, поздняя вечеринка здесь, но я недавно опубликовал ответ с использованием генераторов и co
библиотека, которая ответила бы на этот вопрос ( см. решение 2):
Код будет выглядеть примерно так:
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);
Вы в значительной степени напишите синхронный код, который был бы в действительности асинхронным!
Надеюсь, что это поможет!
Ответ 8
Слишком поздно, но может помочь другим, как я
doSomeThingAsync()
.then(x => {
if(condition){
throw x
}
else{
return x
}
})
.then()
.then()
.catch(x => {
if(! (x instanceof Error )){
return x
}
else{
throw x
}
})
.catch(errorHandler)
Ответ 9
Попробуй использовать libs вот так:
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});