Предотвращение запуска функции, если параллельная функция уже завершилась с ошибкой (используя jQuery $.when())
У меня есть довольно запутанная серия асинхронных функций, которые я объединяю в цепочку с помощью jQuery Deferreds.
По сути, у меня есть два параллельных потока функций, за которыми следуют функции, которые должны выполняться последовательно, когда параллельные потоки завершены. Вот пример (это вызовы AJAX в реальной жизни):
function makeOmelette() {
$.when(
crackEggs().then(beatEggs),
washTomatos().then(chopTomatos)
).then(mixEggsAndTomatos).then(fryEggsAndTomatos).done(function() {
msg('Omelette is ready!');
}).fail(function() {
msg('Oh no, omelette fail!');
});
}
Обработчики done() и fail() в конце имеют дело с успехом или неудачей. Я хотел бы, чтобы обработчик fail() выполнялся, как только что-то пойдет не так, в любом месте цепочки, и чтобы все дальнейшее выполнение было остановлено.
В основном это работает - кроме одного случая. Если при взломе яиц произошел сбой, то помидоры все еще вымыты и нарезаны - даже если сбой уже произошел к тому времени, как началось выполнение одной из этих задач. Точно так же, если при мытье помидоров происходит сбой, это не мешает запуску функций crackEggs и beatEggs.
Вы можете увидеть пример здесь.
Меня беспокоит это то, что в моем реальном жизненном коде я не хочу, чтобы браузер выполнял AJAX-вызовы на сервер, что могло бы потенциально изменить состояние, если в этом нет никакого смысла, потому что общий процесс уже завершился неудачей.
Есть ли способ сделать то, что я хочу? Я полагаю, что мог бы прикрепить обработчики сбоев к каждой из параллельных задач в отдельности, но мне было интересно, есть ли менее многословный / повторяющийся способ.
Спасибо!
2 ответа
Добавить переменную в статусы. например, внутри done( var status = 1 )
и внутри fail( var status = 2
затем поместите простое выражение перед запуском параллельной функции, чтобы она не запускалась, если status = 2
,
Попробуй (этот паттерн)
HTML
<input type="button" value="reject" />
<input type="button" value="resolve" /> <output></output> <output></output>
<div id="messages"></div>
JS
$(function () {
$("input").on("click", function (e) {
var dfd = new $.Deferred();
$("#messages").children().remove();
makeOmelette(this.value);
function makeOmelette(resolveReject, dfd) {
// dfd = dfd;
$.when(
// `crackEggs("resolve")`, `crackEggs("reject")`
crackEggs(resolveReject),
washTomatos())
.done(function (a, b) {
if (a === "rejected") {
err();
};
if (a === "resolved") {
beatEggs();
chopTomatos();
mixEggsAndTomatos();
fryEggsAndTomatos();
success();
};
});
};
function success() {
dfd.done(function (s) {
msg('Omelette is ready!');
$("output:first").val(s);
})
};
function err() {
dfd.fail(function (e) {
msg('Oh no, omelette fail!');
$("output:first").val(e);
});
};
dfd.always(function () {
$("output:last").val(dfd.state() + " at " + $.now())
});
function crackEggs(_resolveReject) {
(_resolveReject === "reject"
? dfd.reject(_resolveReject + " called at " + $.now())
: dfd.resolve(_resolveReject + " called at " + $.now())
);
msg('Cracking eggs');
return dfd.state();
}
function beatEggs() {
msg('Beating eggs');
return dfd.state();
}
function washTomatos() {
msg('Washing tomatos');
return dfd.state();
}
function chopTomatos() {
msg('Chopping tomatos');
return dfd.state();
}
function mixEggsAndTomatos() {
msg('Mixing eggs and tomatos');
return dfd.state();
}
function fryEggsAndTomatos() {
msg('Frying eggs and tomatos');
return dfd.state();
}
function msg(text) {
$('#messages').append('<p>' + text + '</p>');
}
});
});