Когда есть. То (успех, неудача) считается антипаттерном для promises?

Я рассмотрел рекомендацию по обещанию bluebird, в которой упоминается, что .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) })

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

Ответ 1

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

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

Здесь диаграмма управляющего потока:

control flow diagram of then with two argumentscontrol flow diagram of then catch chain

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

// 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);
}

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

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

Я не совсем понимаю его объяснение, как для try and catch

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

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


Что не так с этим?

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

Чтобы повторить обратный вызов. Вы скорее хотите

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

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

Ответ 2

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

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

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

Ответ 3

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

Catch Approach

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

<сильные > Преимущества

  • Все ошибки обрабатываются одним блоком catch.
  • Даже улавливает любое исключение в блоке then.
  • Цепочка множественных обратных вызовов успеха

Недостатки

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

Подход к успеху/ошибкам

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

<сильные > Преимущества

  • Вы получаете мелкозернистый контроль ошибок.
  • У вас может быть обычная функция обработки ошибок для различных категорий ошибок, таких как ошибка db, ошибка 500 и т.д.

Disavantages

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

Ответ 4

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

В ES2018

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

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

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

promise.then(f1).catch(f2)

равняется

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

Ответ 5

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

Ответ 6

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

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'); }
)