Ваша блок-схема - это логика, которую вы хотите достичь, но не совсем так, как работают обещания.Проблема в том, что нет способа указать цепочке обещаний просто «закончить» прямо здесь и не вызывать никаких других обработчиков .then()
или .catch()
позже в цепочке.Если вы получите отказ в цепочке и оставите его отклоненным, он вызовет следующий обработчик .catch()
в цепочке.Если вы обрабатываете отклонение локально и не перебрасываете его, тогда он вызовет следующий обработчик .then()
в цепочке.Ни один из этих параметров точно не соответствует вашей логической диаграмме.
Итак, вы должны мысленно изменить то, как вы думаете о своей логической диаграмме, чтобы вы могли использовать цепочку обещаний.
Самый простой вариант (чтовероятно, используется для 90% цепочек обещаний) означает просто поместить один обработчик ошибок в конец цепочки.Любая ошибка в любом месте цепочки просто переходит к единственному обработчику .catch()
в конце цепочки.К вашему сведению, в большинстве случаев я нахожу код более читабельным с .catch()
, чем со вторым аргументом .then()
, поэтому я показал его здесь
firstRequest().then(secondRequest).then(r => {
console.log('both requests successful');
}).catch(err => {
// An error from either request will show here
console.log(err);
});
Когда вы предоставляете уловзаблокировать, и вы не вернете отклоненное обещание или повторно сгенерируете ошибку, тогда инфраструктура обещания думает, что вы «обработали» обещание, поэтому цепочка продолжается в соответствии с решением.Если вы сбросите ошибку, то следующий блок перехвата сработает, и любые промежуточные обработчики .then()
будут пропущены.
Вы можете использовать это для локального отлова ошибки, сделать что-то (например, зарегистрировать ее) изатем сбросьте его, чтобы цепочка обещаний была отклонена.
firstRequest().catch(e => {
console.log('firstRequest fail', e));
e.logged = true;
throw e;
}).then(r => {
console.log('firstRequest success', r);
return secondRequest();
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
if (!e.logged) {
console.log('secondRequest fail', e));
}
});
Или версия, которая помечает объект ошибки сообщением об отладке, а затем сбрасывает и может регистрировать ошибки только в одном месте:
firstRequest().catch(e => {
e.debugMsg = 'firstRequest fail';
throw e;
}).then(r => {
console.log('firstRequest success', r);
return secondRequest().catch(e => {
e.debugMsg = 'secondRequest fail';
throw e;
});
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
console.log(e.debugMsg, e);
});
У меня даже были ситуации, когда небольшая вспомогательная функция спасала мне некоторый код и некоторую визуальную сложность, особенно если в цепочке их несколько:
function markErr(debugMsg) {
return function(e) {
// mark the error object and rethrow
e.debugMsg = debugMsg;
throw e;
}
}
firstRequest()
.catch(markErr('firstRequest fail'))
.then(r => {
console.log('firstRequest success', r);
return secondRequest().catch(markErr('secondRequest fail'));
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
console.log(e.debugMsg, e);
});
Принимаякаждый из ваших вопросов в отдельности:
Как реализовать описанную логику в соответствии с лучшими практиками всех обещаний?
Описано выше.Я бы сказал, что самая простая и лучшая практика - это самый первый блок кода, который я показываю.Если вам нужно убедиться в том, что когда вы доберетесь до финального .catch()
, у вас будет уникально идентифицируемая ошибка, чтобы вы знали, какой шаг вызвал ее, то измените отклоненную ошибку в каждой отдельной функции, чтобы она была уникальной, чтобы вы могли определить, из какойодин .catch()
блок в конце.Если вы не можете изменить эти функции, вы можете обернуть их оболочкой, которая ловит и помечает их ошибку, или вы можете сделать это в соответствии с решением типа markErr()
, которое я показал.В большинстве случаев вам просто нужно знать, что произошла ошибка, а не точный шаг, на котором она произошла, поэтому обычно не требуется для каждого шага в цепочке.
Можно ли избежать броскае в каждом блоке улова?
Это зависит.Если объекты ошибок уже уникальны, то вы можете просто использовать один .catch()
в конце.Если объекты ошибок не являются уникальными, но вам необходимо знать, какой именно шаг не удался, то вы должны либо использовать .catch()
на каждом шаге, чтобы можно было однозначно пометить ошибку, либо вам нужно изменить каждую функцию в цепочке, чтобыуникальная ошибка.
Должен ли я использовать обещания es6?
Да.Нет лучшего способа, о котором я знаю.
Или лучше использовать библиотеку обещаний?
Мне неизвестны какие-либо функции в библиотеке обещаний, которые могли бы сделатьэто проще.Это на самом деле просто о том, как вы хотите сообщить об ошибках и о том, определяет ли каждый шаг уникальную ошибку или нет.Библиотека обещаний не может сделать это для вас.
Любой другой совет?
Продолжайте узнавать больше о том, как сформировать обещания в решение для каждой отдельной проблемы.