Размещение улова ДО и ПОСЛЕ


105

Мне сложно понять разницу между помещением .catchBEFORE и AFTER во вложенное обещание.

Альтернатива 1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

Альтернатива 2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

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

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

В каждой функции я console.log('Running test X')проверяю, выполняется ли она.

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


и .then, и .catch могут изменить обещание ... поэтому я не уверен, откуда взялось непонимание. Если вы поместите catch перед .then, это будет перехватывать отклонения, которые произошли до .then, а .then будет запускать обратные вызовы done / fail на основе того, что происходит внутри .catch, и наоборот, когда вы меняете их местами.
Kevin B

Извините, если мой вопрос был непонятен. Но в этом случае, как я уже сказал, оба случая ведут себя одинаково, поэтому я не вижу разницы. Можете ли вы сказать мне, когда мы ставим улов ДО, а когда мы решили положить его ПОСЛЕ? ставить это после кажется действительно интуитивно понятным и обычным. Просто не знаю, почему иногда мы ставим это раньше,
Занко

Если они работают одинаково, это просто потому, что то, что они делают, не влияет на результат в данном конкретном случае. Незначительное изменение любого из них могло повлиять на результат.
Кевин Би

Что значит «изменить результат»? Извините, я действительно
Занко

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

Ответы:


242

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

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

и

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

Существуют различия, когда p разрешает или отклоняет, но значение этих различий зависит от того, что делает код внутри обработчиков .then()or .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).


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

9
@ArneHugo - Хорошее предложение. Я добавил.
jfriend00

Итак, во время цепочки обещаний мы можем писать сценарии .then .catch .catch .then?
Капил Рагхуванши

@KapilRaghuwanshi, да, вы можете использовать его для передачи значения по умолчанию в случае сбоя. т.е. Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)и Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov - Как я догадался, это неправильно .then(this.setState({isModalOpen: false})). Вы не передаете ссылку на функцию, .then()чтобы код в скобках выполнялся немедленно (до разрешения обещания). Так и должно быть .then(() => this.setState({isModalOpen: false})).
jfriend00

32

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

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()повторяется, то then()не будет вызываться, поскольку исключение будет брошено вызывающему немедленно. Если handleCatch()удастся изящно then()решить проблему, будет вызван.


это хорошее объяснение, но вы могли бы обернуть сироту then()вfinally{...}
тыскр

2
@ 82Tuskers, ты уверен? Если я ставлю then()в finally{...}, не будет его неправильно назвать , даже если handleCatch()броски? Имейте в виду, что моей целью было показать аналогичный синхронный код, а не предлагать различные способы обработки исключений
akivajgordon

Итак, если мы хотим обрабатывать все случаи, но по-прежнему цеплять .then (), было бы лучше использовать .then (сделать что-то) .catch (зарегистрировать ошибку и обновить состояние) .then (сделать другое) .catch (log err) где мы пытаемся поймать каждую точку, но при этом продолжаем выполнять команды?
Анна
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.