Error al volver a lanzar en la captura de promesa

88

Encontré el siguiente código en un tutorial:

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

Estoy un poco confundido: ¿la llamada de captura logra algo? Me parece que no tiene ningún efecto, ya que simplemente arroja el mismo error que se detectó. Baso esto en cómo funciona un intento / captura regular.

Tyler Durden
fuente
¿Podría proporcionar un enlace al tutorial? Tal vez haya un contexto adicional que sería útil ...
Igor
@Igor No puedo, está en Pluralsight. ¿Es esto posiblemente solo un marcador de posición para alguna lógica de manejo de errores?
Tyler Durden
Eso es lo que supongo, ya que no hace nada más que pasar el error a la persona que llama, lo que también podría lograrse al no tener la captura para empezar.
Igor
1
@TylerDurden Sospecho que tienes razón al decir que es un marcador de posición.
Jared Smith
@TylerDurden, también creo que es un marcador de posición. Tal vez tratando de demostrar cómo formatear / normalizar errores. Básicamente, la promesa equivalente a try { ... }catch(error){ throw new Error("something went wrong") }. O para demostrar que las promesas y los errores son compatibles (al menos en ese sentido) . Pero en su implementación actual es simplemente estúpido. Tienes razón, no hace nada y ni siquiera es como un gancho que agregarías en OOP para permitir sobrescribirlo en una clase heredada. Agregaría el bloque de captura tan pronto como haga algo, pero no así, no solo como un marcador de posición.
Thomas

Respuestas:

124

No tiene sentido atrapar y lanzar como muestra. No hace nada útil excepto agregar código y ejecución lenta. Por lo tanto, si va a .catch()volver a lanzar, debe haber algo que desee hacer en el .catch(), de lo contrario, debe eliminar por .catch()completo.

El punto habitual para esa estructura general es cuando desea ejecutar algo .catch()como registrar el error o limpiar algún estado (como cerrar archivos), pero desea que la cadena de promesa continúe como rechazada.

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

En un tutorial, puede estar ahí solo para mostrar a las personas dónde pueden detectar errores o para enseñar el concepto de cómo manejar el error y luego volver a lanzarlo.


Algunas de las razones útiles para atrapar y volver a lanzar son las siguientes:

  1. Desea registrar el error , pero mantenga la cadena de promesa como rechazada.
  2. Quieres convertir el error en otro error (a menudo para facilitar el procesamiento de errores al final de la cadena). En este caso, volvería a generar un error diferente.
  3. Desea realizar un montón de procesamiento antes de que continúe la cadena de promesas (como recursos cerrados / libres) pero desea que la cadena de promesas permanezca rechazada.
  4. Desea un lugar para colocar un punto de interrupción para el depurador en este punto de la cadena de promesa si hay una falla.

Pero, una simple captura y repetición del mismo error sin otro código en el controlador de captura no hace nada útil para la ejecución normal del código.

jfriend00
fuente
En mi opinión, no es un buen ejemplo. Con este enfoque, es fácil obtener registros múltiples para 1 error. En Java, simplemente throw new Exception(periousException);no sé si JavaScript admite errores anidados, pero de todos modos "registrar y lanzar" es una mala práctica.
Cherry
26
@Cherry - No se puede decir que esta sea una mala práctica en general. Hay ocasiones en las que un módulo desea registrar sus propios errores a su manera y esta es una forma de hacerlo. Además, no estoy recomendando esto, solo estoy explicando que no hay razón para tener un .catch()error y arrojar el mismo error dentro de la captura a menos que haga ALGO más en el archivo .catch(). Ese es el punto de esta respuesta.
jfriend00
Generalmente, las excepciones deben ajustarse al nivel de abstracción. Está perfectamente bien capturar una excepción relacionada con la base de datos, por ejemplo, y lanzar algo como una excepción de "servicio" que será manejada por la persona que llama. Esto es especialmente útil cuando no sabe qué exponer detalles sobre excepciones de bajo nivel
maxTrialfire
3
Otra buena razón para atrapar y (a veces) lanzar es para manejar un error específico, pero volver a lanzar todo lo demás.
Jasper
2
@SimonZyx - Sí, .finally()puede ser muy útil para eso, pero a veces los recursos ya están atendidos en la ruta sin errores, por .catch()lo que sigue siendo el lugar para cerrarlos. Realmente depende de la situación.
jfriend00
15

Ambos métodos .then()y .catch()devuelven Promesas, y si lanza una Excepción en cualquiera de los manejadores, la promesa devuelta se rechaza y la Excepción se capturará en el siguiente manejador de rechazo.

En el siguiente código, lanzamos una excepción en el primero .catch(), que se captura en el 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);
});

El segundo .catch()devuelve un Promised que se cumple, .then()se puede llamar al manejador:

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

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

¡Espero que esto ayude!

Philippe Sultan
fuente
4

No hay una diferencia importante si omite completamente la catchllamada al método.

Lo único que agrega es una micro-tarea adicional, lo que en la práctica significa que notará el rechazo de la promesa más tarde que en el caso de una promesa que falla sin la catchcláusula.

El siguiente fragmento demuestra esto:

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 cómo se informa el segundo rechazo antes que el primero. Esa es la única diferencia.

trincot
fuente
3

Entonces parece que su pregunta es: "En la cadena de promesas, ¿qué hace el .catch()método?"

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

La instrucción throw "se detendrá (las instrucciones después de throw no se ejecutarán), y el control se pasará al primer bloque catch en la pila de llamadas. Si no existe un bloque catch entre las funciones del llamador, el programa terminará".

En la cadena de promesa, el .then()método devolverá algún tipo de fragmento de datos. Este regreso del trozo completará la promesa. La devolución exitosa de los datos completa la promesa. Puedes pensar en el .catch()método de la misma manera. .catch()sin embargo, manejará recuperaciones de datos fallidas. La declaración throw completa la promesa. De vez en cuando, verá que los desarrolladores usan .catch((err) => {console.log(err))} lo que también completaría la cadena de promesas.

Matt Fernandez
fuente
0

En realidad, no necesita volver a lanzarlo, simplemente deje el Promise.catch vacío; de lo contrario, se considerará como no manejar el rechazo y luego envolverá el código en un intento de captura y detectará el error automáticamente que se está transmitiendo.

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
fuente
0

En la cadena de promesas, es mejor usar .catch

ex en la función f2: .entonces (...). captura (e => rechazar (e));

  • test1 - con try catch
  • test2 - sin try o .catch
  • test3 - con .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
fuente