Когда.then(успех, провал) считается антипаттерном для обещаний?

Я взглянул на FAQ об обещании синей птицы, в котором упоминается, что .then(success, fail) это антипаттерн. Я не совсем понимаю его объяснение, что касается попытки поймать. Что не так с этим следующим?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

Кажется, что пример предлагает следующее как правильный путь.

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

Какая разница?

5 ответов

Какая разница?

.then() call возвратит обещание, которое будет отклонено в случае, если обратный вызов выдаст ошибку. Это значит, когда твой успех logger терпит неудачу, ошибка будет передана следующему .catch() обратный вызов, но не на fail обратный вызов, который идет вместе с success,

Вот схема потока управления:

схема управления тогда с двумя аргументамисхема управления цепью захвата

Чтобы выразить это в синхронном коде:

// some_promise_call().then(logger.log, logger.log)
then: {
    try {
        var results = some_call();
    } catch(e) {
        logger.log(e);
        break then;
    } // else
        logger.log(results);
}

Второй log (что походит на первый аргумент .then()) будет выполняться только в том случае, если исключение не произошло. Помеченный блок и break заявление кажется немного странным, это на самом деле то, что есть в Python try-except-else для(рекомендуется чтение!).

// some_promise_call().then(logger.log).catch(logger.log)
try {
    var results = some_call();
    logger.log(results);
} catch(e) {
    logger.log(e);
}

catchlogger также будет обрабатывать исключения из успешного вызова журнала.

Так много для разницы.

Я не совсем понимаю его объяснение относительно попытки поймать

Аргумент заключается в том, что обычно вы хотите отлавливать ошибки на каждом этапе обработки и не должны использовать его в цепочках. Предполагается, что у вас есть только один конечный обработчик, который обрабатывает все ошибки, в то время как при использовании "antipattern" ошибки в некоторых из обратных вызовов не обрабатываются.

Однако этот шаблон на самом деле очень полезен: когда вы хотите обработать ошибки, которые произошли именно на этом шаге, и вы хотите сделать что-то совершенно иное, когда ошибка не произошла - то есть, когда ошибка не устраняется. Помните, что этоветвит ваш поток управления. Конечно, это иногда желательно.


Что не так с этим следующим?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

Что вам пришлось повторить ваш обратный звонок. Ты скорее хочешь

some_promise_call()
   .catch(function(e) {
       return e; // it's OK, we'll just log it
   })
   .done(function(res) {
       logger.log(res);
   });

Вы также можете рассмотреть возможность использования.finally() за это.

Два не совсем идентичны. Разница в том, что первый пример не поймает исключение, которое выдается в вашем success обработчик. Так что, если ваш метод должен только когда-либо возвращать разрешенные обещания, как это часто бывает, вам нужен трейлинг catch обработчик (или еще один then с пустым success параметр). Конечно, может быть, что ваш then обработчик не делает ничего, что потенциально может дать сбой, и в этом случае используется один 2-параметр then может быть в порядке.

Но я считаю, что смысл текста, на который вы ссылаетесь, заключается в том, что then в основном полезен по сравнению с обратными вызовами в своей способности связывать несколько асинхронных шагов, и когда вы действительно делаете это, 2-параметрическая форма then тонко ведет себя не совсем так, как ожидалось, по вышеуказанной причине. Это особенно нелогично, когда используется средняя цепь.

Как человек, который сделал много сложных асинхронных вещей и столкнулся с такими углами больше, чем я хотел бы признать, я действительно рекомендую избегать этого анти-паттерна и придерживаться подхода отдельного обработчика.

Рассматривая преимущества и недостатки обоих, мы можем сделать расчетное предположение относительно того, что подходит для данной ситуации. Это два основных подхода к выполнению обещаний. У обоих есть свои плюсы и минусы

Поймать подход

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

преимущества

  1. Все ошибки обрабатываются одним блоком перехвата.
  2. Даже ловит любое исключение в тогдашнем блоке.
  3. Цепочка нескольких успешных обратных вызовов

Недостатки

  1. В случае создания цепочки становится трудно отображать разные сообщения об ошибках.

Подход Успех / Ошибка

some_promise_call()
.then(function success(res) { logger.log(res) },
      function error(err) { logger.log(err) })

преимущества

  1. Вы получаете детальный контроль ошибок.
  2. Вы можете иметь общую функцию обработки ошибок для различных категорий ошибок, таких как ошибка db, ошибка 500 и т. Д.

Disavantages

  1. Вам все еще понадобится другой catch если вы хотите обработать ошибки, вызванные обратным вызовом успеха

Простое объяснение:

В ES2018

Когда метод catch вызывается с аргументом onRejected, предпринимаются следующие шаги:

  1. Пусть обещание будет этим значением.
  2. Вернуть? Invoke(обещание, "затем", "неопределено, onRejected").

это означает:

promise.then(f1).catch(f2)

равняется

promise.then(f1).then(undefiend, f2)

С помощью .then().catch() позволяет вам включить Promise Chaining, которая необходима для выполнения рабочего процесса. Возможно, вам понадобится прочитать некоторую информацию из базы данных, затем вы захотите передать ее асинхронному API, а затем захотите манипулировать ответом. Вы можете отправить ответ обратно в базу данных. Обработка всех этих рабочих процессов с вашей концепцией выполнима, но очень сложна в управлении. Лучшее решение будет then().then().then().then().catch() который получает все ошибки всего за один раз и позволяет вам сохранить удобство сопровождения кода.

С помощью then() а также catch() помогает связать успех и обработчик неудач в обещании.catch() работает по обещанию, возвращенному then(). Он обрабатывает,

  1. Если обещание было отклонено. Смотрите #3 на картинке
  2. Если ошибка произошла в обработчике успеха then(), между номерами строк с 4 по 7 ниже. См. № 2.a на картинке (обратный вызов при сбое наthen() не справляется с этим.)
  3. Если ошибка произошла в обработчике ошибок then(), строка номер 8 ниже. См. № 3.b на рисунке.

1. let promiseRef: Promise = this. aTimetakingTask (false); 2. promiseRef 3. .then( 4. (result) => { 5. /* successfully, resolved promise. 6. Work on data here */ 7. }, 8. (error) => console.log(error) 9. ) 10. .catch( (e) => { 11. /* successfully, resolved promise. 12. Work on data here */ 13. });

Примечание. Часто обработчик сбоев может не определяться, еслиcatch()уже написано. РЕДАКТИРОВАТЬ:reject() привести к вызову catch() только если обработчик ошибок в then()это не определено. Уведомление №3 на картинке кcatch(). Он вызывается, когда обработчик в строках №8 и 9 не определен.

Это имеет смысл, потому что обещание, возвращаемое then() не имеет ошибки, если обратный вызов позаботится об этом.

Вместо слов хороший пример. Следующий код (если первое обещание разрешено):

Promise.resolve()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

идентичен:

Promise.resolve()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)

Но с отклоненным первым обещанием это не идентично:

Promise.reject()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

Promise.reject()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)
Другие вопросы по тегам