Tenho dificuldade em entender a diferença entre colocar .catch
ANTES e DEPOIS em uma promessa aninhada.
Alternativa 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);
});
Alternativa 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);
});
O comportamento de cada função é o seguinte: test1 falha se número for <0
test2 falha se número for > 10
e test3 falha se número não for 100
. Neste caso, test2 está apenas falhando.
Tentei rodar e fazer o test2Async falhar, tanto ANTES quanto DEPOIS então se comporta da mesma forma e que não está executando o test3Async. Alguém pode me explicar a principal diferença para colocar a captura em lugares diferentes?
Em cada função eu console.log('Running test X')
para verificar se ela é executada.
Esta questão surge devido ao tópico anterior que publiquei, Como transformar o retorno de chamada aninhado em promessa? . Acho que é um problema diferente e vale a pena postar outro tópico.
fonte
Respostas:
Então, basicamente você está perguntando qual é a diferença entre esses dois (onde
p
está uma promessa criada a partir de algum código anterior):e
Existem diferenças quando p resolve ou rejeita, mas se essas diferenças importam ou não depende do que o código dentro dos manipuladores
.then()
ou.catch()
faz.O que acontece quando
p
resolve:No primeiro esquema, quando
p
resolve, o.then()
manipulador é chamado. Se esse.then()
manipulador retornar um valor ou outra promessa que eventualmente seja resolvida, o.catch()
manipulador será ignorado. Mas, se o.then()
manipulador lança ou retorna uma promessa que eventualmente rejeita, então o.catch()
manipulador executará para uma rejeição na promessa originalp
, mas também para um erro que ocorre no.then()
manipulador.No segundo esquema, quando
p
resolve, o.then()
manipulador é chamado. Se esse.then()
manipulador lança ou retorna uma promessa que eventualmente rejeita, então o.catch()
manipulador não pode capturar isso porque está antes dele na cadeia.Então, essa é a diferença # 1. Se o
.catch()
manipulador for AFTER, ele também poderá detectar erros dentro do.then()
manipulador.O que acontece quando
p
rejeita:Agora, no primeiro esquema, se a promessa for
p
rejeitada, o.then()
manipulador será ignorado e o.catch()
manipulador será chamado conforme o esperado. O que você faz no.catch()
manipulador determina o que é retornado como o resultado final. Se você apenas retornar um valor do.catch()
manipulador ou retornar uma promessa que eventualmente é resolvida, a cadeia de promessas muda para o estado resolvido porque você "tratou" o erro e retornou normalmente. Se você lançar ou retornar uma promessa rejeitada no.catch()
manipulador, a promessa retornada permanecerá rejeitada.No segundo esquema, se a promessa for
p
rejeitada, o.catch()
manipulador será chamado. Se você retornar um valor normal ou uma promessa que eventualmente seja resolvida do.catch()
manipulador ("manipulando" assim o erro), a cadeia de promessas muda para o estado resolvido e o.then()
manipulador após o.catch()
será chamado.Então essa é a diferença # 2. Se o
.catch()
manipulador for ANTES, ele pode manipular o erro e permitir que o.then()
manipulador ainda seja chamado.Quando usar qual:
Use o primeiro esquema se desejar apenas um
.catch()
manipulador que possa detectar erros na promessa originalp
ou no.then()
manipulador e uma rejeição dep
deve ignorar o.then()
manipulador.Use o segundo esquema se quiser ser capaz de detectar erros na promessa original
p
e talvez (dependendo das condições), permitir que a cadeia de promessa continue como resolvida, executando assim o.then()
manipulador.A outra opção
Há uma outra opção para usar os dois retornos de chamada que você pode passar para
.then()
:Isso garante que apenas um de
fn1
oufn2
será chamado. Sep
resolver, entãofn1
será chamado. Sep
rejeitar, entãofn2
será chamado. Nenhuma mudança de resultadofn1
pode fazer com quefn2
seja chamado ou vice-versa. Portanto, se você deseja ter certeza absoluta de que apenas um de seus dois manipuladores é chamado, independentemente do que acontece nos próprios manipuladores, você pode usarp.then(fn1, fn2)
.fonte
.then()
e.catch()
, que você responde. Além disso, você dá algumas dicas de quando usar essa ordem, onde acho apropriado mencionar uma terceira opção, ou seja, passar o manipulador de sucesso e de erro para .then () . Nesse caso, no máximo, um manipulador será chamado.Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)
ePromise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
.then(this.setState({isModalOpen: false}))
. Você não está passando uma referência de função para.then()
para que o código nos parênteses seja executado imediatamente (antes que a promessa seja resolvida). Deve ser.then(() => this.setState({isModalOpen: false}))
.A resposta de jfriend00 é excelente, mas achei que seria uma boa ideia adicionar o código síncrono análogo.
é semelhante ao síncrono:
Se
iMightThrow()
não jogar,then()
será chamado. Se ele lançar (ou sethen()
ele próprio lançar),handleCatch()
será chamado. Observe como ocatch
bloco não tem controle sobre se é ou nãothen
chamado.Por outro lado,
é semelhante ao síncrono:
Neste caso, se
iMightThrow()
não lançar, entãothen()
executará. Se lançar, então caberiahandleCatch()
a decidir sethen()
é chamado, porque sehandleCatch()
relançar, entãothen()
não será chamado, já que a exceção será lançada para o chamador imediatamente. SehandleCatch()
puder lidar com o problema normalmente,then()
será chamado.fonte
then()
em umfinally{...}
then()
nofinally{...}
, não seria incorretamente ser chamado mesmo sehandleCatch()
joga? Tenha em mente que meu objetivo era mostrar código síncrono análogo, não sugerir maneiras diferentes de lidar com exceções