javascript node.js promise try-catch

javascript - ¿Por qué no se recomienda `.catch(err=> console.error(err))`?



node.js promise (5)

Estoy usando promesas y tengo un código que se parece a lo siguiente:

function getStuff() { return fetchStuff().then(stuff => process(stuff) ).catch(err => { console.error(err); }); }

O:

async function getStuff() { try { const stuff = await fetchStuff(); return process(stuff); } catch (err) { console.error(err); } }

Estaba haciendo esto para evitar faltar en errores, pero otro usuario me dijo que no debería estar haciendo esto y que está mal visto.

  • ¿Qué tiene de malo el return ….catch(err => console.error(err)) ?
  • He visto un montón de código que hace esto, ¿por qué?
  • ¿Qué debo hacer en su lugar?

¿Qué tiene de malo el return ….catch(err => console.error(err)) ?

Devuelve una promesa que se cumplirá con undefined después de que manejó el error.

Por sí solo, detectar errores y registrarlos está bien al final de una cadena de promesa:

function main() { const element = document.getElementById("output"); getStuff().then(result => { element.textContent = result; }, error => { element.textContent = "Sorry"; element.classList.add("error"); console.error(error); }); element.textContent = "Fetching…"; }

Sin embargo, si getStuff() el error en sí mismo para registrarlo y no hace nada para manejarlo, ya que proporciona un resultado de retorno razonable, lleva a que aparezca undefined en la página en lugar de " Lo siento ".

He visto un montón de código que hace esto, ¿por qué?

Históricamente, las personas temían que los errores de promesa no se manejaran en ninguna parte, lo que los llevó a desaparecer por completo, ya que la promesa los "tragó". Así que agregaron .catch(console.error) en cada función para asegurarse de que notaran errores en la consola.

Esto ya no es necesario, ya que todas las implementaciones de promesa modernas pueden detectar rechazos de promesas no manejadas y dispararán advertencias en la consola.

Por supuesto, aún es necesario (o al menos una buena práctica, incluso si no espera que algo falle) para detectar errores al final de la cadena de la promesa (cuando ya no devuelve una promesa).

¿Qué debo hacer en su lugar?

En las funciones que return una promesa a su interlocutor, no registre errores y tráguelos haciendo eso. Simplemente devuelva la promesa para que la persona que llama pueda detectar el rechazo y manejar el error de manera adecuada (al iniciar sesión o cualquier otra cosa).

Esto también simplifica mucho el código:

function getStuff() { return fetchStuff().then(stuff => process(stuff)); }

async function getStuff() { const stuff = await fetchStuff(); return process(stuff); }

Si insiste en hacer algo con el motivo de rechazo (registro, modificación de información), asegúrese de volver a lanzar un error:

function getStuff() { return fetchStuff().then(stuff => process(stuff) ).catch(error => { stuffDetails.log(error); throw new Error("something happened, see detail log"); }); }

async function getStuff() { try { const stuff = await fetchStuff(); return process(stuff); } catch(error) { stuffDetails.log(error); throw new Error("something happened, see detail log"); } }

Lo mismo si estás manejando algunos de los errores:

function getStuff() { return fetchStuff().then(stuff => process(stuff) ).catch(error => { if (expected(error)) return defaultStuff; else throw error; }); }

async function getStuff() { try { const stuff = await fetchStuff(); return process(stuff); } catch(error) { if (expected(error)) return defaultStuff; else throw error; } }


¿Por qué el código antiguo hace esto?

Históricamente, las bibliotecas más antiguas (anteriores a 2013) prometen ser "tragadas", rechazadas y rechazadas que usted no ha manejado. Este no ha sido el caso en nada escrito desde entonces.

Que pasa hoy

Los navegadores y Node.js ya registran automáticamente los rechazos de promesas no cobrados o tienen un comportamiento para manejarlos y los registrarán automáticamente.

Además, al agregar el .catch estás señalando al método que llama a la función que se devuelve undefined :

// undefined if there was an error getStuff().then(stuff => console.log(stuff));

La pregunta que uno debería hacerse al escribir un código asíncrono suele ser "¿qué haría la versión síncrona del código?":

function calculate() { try { const stuff = generateStuff(); return process(stuff); } catch (err) { console.error(err); // now it''s clear that this function is ''swallowing'' the error. } }

No creo que un consumidor espere que esta función se devuelva undefined si se produce un error.

Entonces, para resumir, está mal visto porque sorprende a los desarrolladores en el flujo de aplicaciones y los navegadores registran errores de promesa no detectados de todos modos hoy.

Qué hacer en su lugar:

Nada. Esa es la belleza de esto, si escribieras:

async function getStuff() { const stuff = await fetchStuff(); return process(stuff); } // or without async/await const getStuff = fetchStuff().then(process);

En primer lugar, de todas formas obtendrías mejores errores :)

¿Qué hacer si estoy ejecutando una versión antigua de Node.js?

Las versiones anteriores de Node.js podrían no registrar errores o mostrar una advertencia de desaprobación. En esas versiones puede usar console.error (o la instrumentación de registro adecuada) globalmente :

// or throw to stop on errors process.on(''unhandledRejection'', e => console.error(e));


La declaración más general aquí, que se aplica en idiomas más allá de javascript, es que no " detecte " un error a menos que planee " manejar " el error. El registro no está manejando.

es decir, en general, la mejor (¿única?) razón para un retén es manejar / ''lidiar con'' el error de una manera constructiva que permite que el código continúe sin más problemas. Y de nuevo, una línea de registro probablemente nunca logre eso ...


La razón por la que no debe catch errores a menos que sea absolutamente necesario (lo que nunca es) es que

Además de tragar los rechazos de promesa, el controlador de captura también traga cualquier error JS que ocurra en cualquier código sucesivo ejecutado por el controlador de éxito correspondiente.

Trascendencia

  1. Una vez que un controlador de catch detecta un error, se considera como realizado y manejado. Todos los suscriptores de promesa sucesivos en la cadena de promesa llamarán a sus manejadores de éxito en lugar de a los manejadores de fallas o detectores. Esto conduce a comportamientos extraños. Este nunca es el flujo de código previsto.

  2. Si una función en un nivel inferior como un método de servicio ( getStuff ) maneja errores en la catch , rompe el principio de Separación de Preocupaciones . Una responsabilidad del manejador de servicios debe ser únicamente para obtener datos. Cuando esa llamada de datos falla, la aplicación que llama a ese manejador de servicios debe administrar el error.

  3. La captura de errores en alguna función que está siendo capturada por otra, resulta en comportamientos extraños por todos lados y hace que sea realmente difícil rastrear las causas de los errores. Para rastrear estos errores, tenemos que habilitar Break on Caught Exceptions en la consola de desarrollo de Chrome, que se interrumpirá en cada catch y podría demorar horas en finalizar la depuración.

Siempre es una buena práctica manejar los rechazos de promesa, pero siempre debemos hacer eso usando el controlador de failure sobre el controlador de catch . Un controlador de fallas solo detectará los Promise rejections y dejará que la aplicación se rompa, si ocurre algún error de JS, que es como debería ser.


el error es demasiado genérico, es un problema de captura, pero hay tantas cosas con las que la operación fallaría, el error es todo error Algo Específico da granularidad