Erro de relançamento na captura de promessa

88

Encontrei o seguinte código em um tutorial:

promise.then(function(result){
    //some code
}).catch(function(error) {
    throw(error);
});

Estou um pouco confuso: a chamada catch realiza alguma coisa? Parece-me que não tem nenhum efeito, pois simplesmente lança o mesmo erro que foi detectado. Eu baseio isso em como funciona um try / catch regular.

Tyler Durden
fonte
Você poderia fornecer um link para o tutorial? Talvez haja contexto adicional que seria útil ...
Igor
@Igor Não posso, está no Pluralsight. Isso é possivelmente apenas um espaço reservado para alguma lógica de tratamento de erros?
Tyler Durden
Isso é o que eu imagino, já que ele não faz nada mais do que repassar o erro ao chamador, o que também poderia ser feito por não ter o catch para começar.
Igor
1
@TylerDurden Suspeito que você esteja certo sobre isso ser um espaço reservado.
Jared Smith de
@TylerDurden, também acho que é um espaço reservado. Talvez tentando demonstrar como formatar / normalizar erros. Basicamente, a promessa equivalente a try { ... }catch(error){ throw new Error("something went wrong") }. Ou para mostrar que Promessas e Erros são compatíveis (pelo menos dessa forma) . Mas em sua implementação atual é simplesmente estúpido. Você está certo, ele não faz nada e nem mesmo é como um gancho que você adicionaria em OOP para permitir sobrescrevê-lo em uma classe herdada. Eu adicionaria o catch-block assim que fizer algo, mas não assim, não apenas como um marcador de posição.
Thomas

Respostas:

124

Não há sentido em pegar e jogar nu como você mostra. Ele não faz nada útil, exceto adicionar código e execução lenta. Então, se você for .catch()e relançar, deve haver algo que você deseja fazer no .catch(), caso contrário, você deve apenas remover o.catch() inteiramente.

O ponto usual para essa estrutura geral é quando você deseja executar algo no .catch(), como registrar o erro ou limpar algum estado (como fechar arquivos), mas deseja que a cadeia de promessa continue como rejeitada.

promise.then(function(result){
    //some code
}).catch(function(error) {
    // log and rethrow 
    console.log(error);
    throw error;
});

Em um tutorial, pode estar lá apenas para mostrar às pessoas onde eles podem detectar erros ou para ensinar o conceito de como lidar com o erro e, em seguida, relançá-lo.


Algumas das razões úteis para captura e relançamento são as seguintes:

  1. Você quer registrar o erro , mas manter a cadeia de promessa como rejeitada.
  2. Você quer transformar o erro em algum outro erro (geralmente para facilitar o processamento de erros no final da cadeia). Nesse caso, você relançaria um erro diferente.
  3. Você deseja fazer um monte de processamento antes que a cadeia de promessa continue (como recursos fechar / liberar), mas deseja que a cadeia de promessa continue rejeitada.
  4. Você deseja um local para colocar um ponto de interrupção para o depurador neste ponto da cadeia de promessa, se houver uma falha.

Mas, uma simples captura e relançamento do mesmo erro sem nenhum outro código no manipulador de captura não faz nada útil para a execução normal do código.

jfriend00
fonte
Na minha opinião, não é um bom exemplo. Com essa abordagem, você obtém facilmente vários registros para 1 erro. Em java você pode simplesmente throw new Exception(periousException);não saber se o javascript suporta erro aninhado, mas mesmo assim "log e lançar" é uma prática ruim.
Cherry
26
@Cherry - Você não pode dizer que esta é uma prática ruim em geral. Há momentos em que um módulo deseja registrar seus próprios erros de sua própria maneira e esta é uma maneira de fazer isso. Além disso, não estou recomendando isso, estou apenas explicando que não há razão para ter um .catch()e lançar o mesmo erro dentro do catch, a menos que você faça ALGUMA COISA no .catch(). Esse é o ponto desta resposta.
jfriend00
Geralmente, as exceções devem se ajustar ao nível de abstração. É perfeitamente normal capturar uma exceção relacionada ao banco de dados, por exemplo, e lançar algo como uma exceção de "serviço" que será tratada pelo chamador. Isso é especialmente útil quando você não deseja expor detalhes sobre exceções de baixo nível
maxTrialfire
3
Outro bom motivo para capturar e (às vezes) lançar é lidar com um erro específico, mas relançar todo o resto.
Jasper
2
@SimonZyx - Sim, .finally()pode ser muito útil para isso, mas às vezes os recursos já estão sendo cuidados no caminho de não erro, então .catch()ainda é o lugar para fechá-los. Realmente depende da situação.
jfriend00
15

Os métodos .then()e .catch()retornam Promises e, se você lançar uma Exception em qualquer um dos manipuladores, a promessa retornada será rejeitada e a Exception será capturada no próximo manipulador de rejeição.

No código a seguir, lançamos uma exceção no primeiro .catch(), que é capturada no segundo .catch():

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
});

O segundo .catch()retorna um Promised que é cumprido, o .then()manipulador pode ser chamado:

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
})
.then(() => {
    console.log('Show this message whatever happened before');
});

Referência útil: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch

Espero que isto ajude!

Philippe Sultan
fonte
4

Não há diferença importante se você deixar de fora catch chamada de método completamente de fora.

A única coisa que adiciona é uma microtarefa extra, o que na prática significa que você notará a rejeição da promessa mais tarde do que no caso de uma promessa que falha sem o catch cláusula.

O próximo snippet demonstra isso:

var p;
// Case 1: with catch
p = Promise.reject('my error 1')
       .catch(function(error) {
          throw(error);
       });

p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');

p.catch( error => console.log(error) );

Observe como a segunda rejeição é relatada antes da primeira. Essa é a única diferença.

trincote
fonte
3

Portanto, parece que sua pergunta é: "Na cadeia de promessas, o que o .catch()método faz?"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

A instrução throw "irá parar (as instruções após throw não serão executadas) e o controle será passado para o primeiro bloco catch na pilha de chamadas. Se nenhum bloco catch existir entre as funções do chamador, o programa será encerrado."

Na cadeia de promessa, o .then()método retornará algum tipo de bloco de dados. Este retorno do pedaço completará a promessa. O retorno bem-sucedido dos dados completa a promessa. Você pode pensar no .catch()método da mesma maneira. .catch()no entanto, irá lidar com recuperações de dados malsucedidas. A declaração de lançamento completa a promessa. Ocasionalmente, você verá que os desenvolvedores usam o .catch((err) => {console.log(err))} que também completaria a cadeia de promessa.

Matt Fernandez
fonte
0

Na verdade, você não precisa jogá-lo novamente, apenas deixe o Promise.catch vazio, caso contrário ele considerará como não manipulado a rejeição e, em seguida, envolverá o código em um try catch e detectará o erro automaticamente que está ocorrendo.

try{
  promise.then(function(result){
    //some code
  }).catch(function(error) {
    //no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
  });
}catch(e){
  console.log(e);
  //error can handle in here
}
Aylian Craspa
fonte
0

Na cadeia de promessa, é melhor usar .catch

ex na função f2: .então (...). captura (e => rejeitar (e));

  • test1 - com try catch
  • test2 - sem tentar ou .catch
  • test3 - com .catch

function f1() {
    return new Promise((resolve, reject) => {
        throw new Error('test');
    });
}

function f2() {
    return new Promise((resolve, reject) => {
        f1().then(value => {
            console.log('f1 ok ???');
        }).catch(e => reject(e));
    });
}

function test1() {
    console.log('test1 - with try catch - look in F12');
    try {
      f2().then(() => { // Uncaught (in promise) Error: test
        console.log('???'); });
    } catch (e) {
      console.log('this error dont catched');
    }
}

function test2() {
    console.log('test2 - without try or .catch - look in F12');
    f2(); // Uncaught (in promise) Error: test
}

function test3() {
  console.log('test3 - with .catch');
  f2().then(value => {
    console.log('??');
  }).catch(e => {
    console.log(' now its ok, error ', e);
  })
}

setTimeout(() => { test1(); 
  setTimeout(() => { test2(); 
    setTimeout(() => { test3(); 
    }, 100);
  }, 100);
}, 100);

Wagner Pereira
fonte