Размещение улова до и после

Мне трудно понять разницу между установкой .catch(function (err) ПЕРЕД и ПОСЛЕ, затем вложенным обещанием. Это

test1Async(10).then(function (lol) {
  return test2Async(22)
    .then(function (lol) {
      return test3Async(100);
    }).catch(function (err) {
      throw "ERROR AFTER THEN";
    });
}).then(function (results) {
  console.log(results);
}).catch(function (err) {
  console.log(err);
});

и этот

test1Async(10).then(function (lol) {
   return test2Async(22)
     .catch(function (err) {
        throw "ERROR BEFORE THEN";
      })
      .then(function (lol) {
        return test3Async(100);
      });
  }).then(function (results) {
    console.log(results);
  }).catch(function (err) {
    console.log(err);
  });

Поведение каждой функции выполняется следующим образом: test1 сбой, если число <0 test2 терпит неудачу, если число > 10 и test3 сбой, если число не 100. В этом случае test2 только терпит неудачу.

Я попытался запустить и сделать сбой test2Async, и BEFORE, и AFTER ведут себя одинаково и не выполняют test3Async. Может ли кто-нибудь объяснить мне основное различие для размещения уловов в разных местах?

В каждой функции я console.log(Запуск теста X), чтобы проверить, выполняется ли она.

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

Ответы

Ответ 1

Итак, в основном вы спрашиваете, в чем разница между этими двумя (где p - это обещание, созданное из предыдущего кода):

return p.then(...).catch(...);

и

return p.catch(...).then(...);

Существуют различия либо в том случае, когда p разрешает или отклоняет, но зависят ли эти различия или нет от того, что делает код внутри обработчиков .then() или .catch().

Что происходит, когда p разрешает:

В первой схеме, когда p разрешается, вызывается обработчик .then(). Если этот обработчик .then() возвращает значение или другое обещание, которое в конечном итоге разрешает, тогда обработчик .catch() пропускается. Но, если обработчик .then() либо выдает, либо возвращает обещание, которое в конечном итоге отклоняется, тогда обработчик .catch() будет выполняться как для отклонения в исходном обещании p, так и в ошибке, выполняемой в обработчике .then().

Во второй схеме, когда p разрешается, вызывается обработчик .then(). Если этот обработчик .then() либо бросает, либо возвращает обещание, которое в конечном итоге отклоняется, тогда обработчик .catch() не может его поймать, поскольку он находится перед ним в цепочке.

Итак, эта разница # 1. Если обработчик .catch() равен AFTER, он также может ловить ошибки внутри обработчика .then().

Что происходит, когда p отклоняет:

Теперь, в первой схеме, если обещание p отклоняется, тогда обработчик .then() пропускается, и обработчик .catch() будет вызван так, как вы ожидали. То, что вы делаете в обработчике .catch(), определяет то, что возвращается в качестве конечного результата. Если вы просто возвращаете значение из обработчика .catch() или возвращаете обещание, которое в конечном итоге разрешается, то цепочка обещаний переключается в разрешенное состояние, потому что вы "обрабатывали" ошибку и возвращались нормально. Если вы отбросили или вернули отклоненное обещание в обработчике .catch(), тогда возвращаемое обещание останется отклоненным.

Во второй схеме, если обещание p отклоняется, вызывается обработчик .catch(). Если вы возвращаете нормальное значение или обещание, которое в конечном итоге разрешается с помощью обработчика .catch() (таким образом, "обрабатывая" ошибку), то цепочка обещаний переключается в разрешенное состояние, а обработчик .then() после вызова .catch().

Так что разница # 2. Если обработчик .catch() BEFORE, он может обрабатывать ошибку и разрешать обработчик .then().

Когда использовать:

Используйте первую схему, если вам нужен только один обработчик .catch(), который может ловить ошибки в исходном обещании p или в обработчике .then(), а отклонение от p должно пропускать обработчик .then().

Используйте вторую схему, если вы хотите уловить ошибки в оригинальном обещании p и, возможно, (в зависимости от условий), разрешить цепочку обещаний продолжить, как разрешено, тем самым выполняя обработчик .then().

Другой вариант

Есть еще один вариант использования обоих обратных вызовов, которые вы можете передать в .then(), как в:

 p.then(fn1, fn2)

Это гарантирует, что только один из fn1 или fn2 будет когда-либо вызван. Если p разрешается, тогда будет вызываться fn1. Если p отклоняется, тогда будет вызываться fn2. Никакое изменение результата в fn1 никогда не может вызвать вызов fn2 или наоборот. Итак, если вы хотите абсолютно убедиться, что только один из ваших двух обработчиков вызывается независимо от того, что происходит в самих обработчиках, вы можете использовать p.then(fn1, fn2).

Ответ 2

jfriend00 answer отлично, но я подумал, что было бы неплохо добавить аналогичный синхронный код.

return p.then(...).catch(...);

похож на синхронный:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

Если iMightThrow() не выбрасывается, будет вызываться then(). Если он бросает (или если сам then() выбрасывает), тогда будет вызываться handleCatch(). Обратите внимание, что блок catch не имеет контроля над вызовом then.

С другой стороны,

return p.catch(...).then(...);

похож на синхронный:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

В этом случае, если iMightThrow() не выбрасывает, тогда будет выполняться then(). Если он выбрасывает, тогда будет handleCatch() решать, вызывается ли then(), потому что если handleCatch() rethrows, то then() не будет вызываться, так как исключение будет немедленно передано вызывающему. Если handleCatch() может изящно решить проблему, тогда будет вызываться then().