Pregunta Node.js Best Practice Exception Handling


Acabo de empezar a probar node.js hace unos días. Me di cuenta de que el Nodo finaliza cada vez que tengo una excepción no controlada en mi programa. Esto es diferente al contenedor del servidor normal al que he estado expuesto, donde solo muere el subproceso trabajador cuando ocurren excepciones no controladas y el contenedor aún podría recibir la solicitud. Esto plantea algunas preguntas:

  • Es process.on('uncaughtException') la única manera efectiva de protegerse contra eso?
  • Será process.on('uncaughtException') captar la excepción no controlada durante la ejecución de procesos asíncronos también?
  • ¿Hay algún módulo que ya esté incorporado (como el envío de correos electrónicos o la escritura en un archivo) que pueda aprovechar en el caso de excepciones no detectadas?

Agradecería cualquier puntero / artículo que me muestre las mejores prácticas comunes para manejar excepciones no detectadas en node.js


645
2017-09-05 16:15


origen


Respuestas:


Actualización: Joyent ahora tiene su propia guía mencionado en esta respuesta. La siguiente información es más un resumen:

Errores "arrojando" de forma segura

Idealmente, nos gustaría evitar los errores no detectados tanto como sea posible, como tal, en lugar de arrojar el error literalmente, en lugar de eso podemos "lanzar" el error de forma segura utilizando uno de los siguientes métodos dependiendo de la arquitectura de nuestro código:

  • Para código síncrono, si ocurre un error, devuelve el error:

    // Define divider as a syncrhonous function
    var divideSync = function(x,y) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by returning it
            return new Error("Can't divide by zero")
        }
        else {
            // no error occured, continue on
            return x/y
        }
    }
    
    // Divide 4/2
    var result = divideSync(4,2)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/2=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/2='+result)
    }
    
    // Divide 4/0
    result = divideSync(4,0)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/0=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/0='+result)
    }
    
  • Para el código basado en devolución de llamada (es decir, asíncrono), el primer argumento de la devolución de llamada es err, si ocurre un error err es el error, si no ocurre un error, entonces err es null. Cualquier otro argumento sigue al err argumento:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    divide(4,2,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/2=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/2='+result)
        }
    })
    
    divide(4,0,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/0=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/0='+result)
        }
    })
    
  • por memorable código, donde el error puede ocurrir en cualquier lugar, en lugar de arrojar el error, ejecute el error evento en cambio:

    // Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
        events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)
    
    // Add the divide function
    Divider.prototype.divide = function(x,y){
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by emitting it
            var err = new Error("Can't divide by zero")
            this.emit('error', err)
        }
        else {
            // no error occured, continue on
            this.emit('divided', x, y, x/y)
        }
    
        // Chain
        return this;
    }
    
    // Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    divider.on('divided', function(x,y,result){
        console.log(x+'/'+y+'='+result)
    })
    
    // Divide
    divider.divide(4,2).divide(4,0)
    

Errores de "captura" de forma segura

Sin embargo, a veces puede existir un código que arroje un error en alguna parte que puede conducir a una excepción no detectada y a una posible falla de nuestra aplicación si no la atrapamos de manera segura. Dependiendo de la arquitectura de nuestro código, podemos usar uno de los siguientes métodos para atraparlo:

  • Cuando sabemos dónde ocurre el error, podemos ajustar esa sección en un dominio node.js

    var d = require('domain').create()
    d.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    
    // catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
        // the asynchronous or synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    })
    
  • Si sabemos dónde está ocurriendo el error es el código sincrónico, y por cualquier razón no puede usar dominios (tal vez la versión anterior del nodo), podemos utilizar la declaración try catch:

    // catch the uncaught errors in this synchronous code block
    // try catch statements only work on synchronous code
    try {
        // the synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    } catch (err) {
        // handle the error safely
        console.log(err)
    }
    

    Sin embargo, tenga cuidado de no usar try...catch en código asíncrono, ya que un error arrojado asincrónicamente no se detectará:

    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }
    

    Otra cosa para tener cuidado con try...catch es el riesgo de envolver su devolución de llamada de finalización dentro del try declaración como tal:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    var continueElsewhere = function(err, result){
            throw new Error('elsewhere has failed')
    }
    
    try {
            divide(4, 2, continueElsewhere)
            // ^ the execution of divide, and the execution of 
            //   continueElsewhere will be inside the try statement
    }
    catch (err) {
            console.log(err.stack)
            // ^ will output the "unexpected" result of: elsewhere has failed
    }
    

    Este problema es muy fácil de hacer a medida que su código se vuelve más complejo. Como tal, es mejor utilizar dominios o devolver errores para evitar (1) excepciones no detectadas en el código asincrónico (2) la captura de captura de captura que no desea. En los idiomas que permiten un enhebrado adecuado en lugar del estilo de máquina de eventos asíncrono de JavaScript, esto no es un problema.

  • Finalmente, en el caso de que ocurra un error no detectado en un lugar que no estaba incluido en un dominio o en una declaración try catch, podemos hacer que nuestra aplicación no se cuelgue al usar el uncaughtException oyente (sin embargo, al hacerlo, puede poner la aplicación en un estado desconocido)

    // catch the uncaught errors that weren't wrapped in a domain or try catch statement
    // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
        // handle the error safely
        console.log(err)
    })
    
    // the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err
    

639
2018-04-18 20:10



Lo que sigue es un resumen y una curaduría de muchas fuentes diferentes sobre este tema, incluyendo ejemplos de códigos y citas de publicaciones de blogs seleccionadas. La lista completa de mejores prácticas se puede encontrar aquí


Mejores prácticas de manejo de errores Node.JS


Número1: promesas de uso para el manejo de errores asíncrono

TL; DR: Manejar errores asíncronos en el estilo de devolución de llamada es probablemente la forma más rápida de ir al infierno (por ejemplo, la pirámide de la perdición). El mejor regalo que le puede dar a su código es, en cambio, usar una biblioteca confiable que ofrezca una sintaxis de código muy compacta y familiar, como try-catch.

De otra manera: El estilo de devolución de llamada de Node.JS, función (err, respuesta), es una forma prometedora de código no-mantenible debido a la combinación de manejo de errores con código casual, anidación excesiva y patrones de codificación incómodos

Ejemplo de código - bueno

doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);

ejemplo de código anti patrón - manejo de errores de estilo de devolución de llamada

getData(someParameter, function(err, result){
    if(err != null)
      //do something like calling the given callback function and pass the error
    getMoreData(a, function(err, result){
          if(err != null)
            //do something like calling the given callback function and pass the error
        getMoreData(b, function(c){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

Cita del blog: "Tenemos un problema con las promesas" (Del blog pouchdb, puesto 11 para las palabras clave "Node Promises")

"... Y de hecho, las devoluciones de llamadas hacen algo aún más siniestro: nos privan de la pila, que es algo que generalmente damos por sentado en los lenguajes de programación. Escribir código sin una pila es muy parecido a conducir un automóvil sin un pedal de freno: no te das cuenta de lo mal que lo necesitas, hasta que lo alcances y no esté allí. El objetivo de las promesas es devolvernos los fundamentos del lenguaje que perdimos cuando fuimos asincrónicos: return, throw y stack. Pero debe saber cómo usar las promesas correctamente para aprovecharlas."


Number2: use solo el objeto Error incorporado

TL; DR: Es bastante común ver código que arroje errores como cadena o como un tipo personalizado; esto complica la lógica de manejo de errores y la interoperabilidad entre módulos. Ya sea que rechace una promesa, genere una excepción o emita un error utilizando Node.JS, el objeto de error incorporado aumenta la uniformidad y evita la pérdida de información de error

De otra manera: Al ejecutar algún módulo, no estar seguro de qué tipo de errores son a cambio, hace que sea mucho más difícil razonar sobre la próxima excepción y manejarlo. Incluso vale la pena, el uso de tipos personalizados para describir los errores puede conducir a la pérdida de información de error crítico como el seguimiento de la pila.

Ejemplo de código: hacerlo bien

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

ejemplo de código anti patrón

//throwing a String lacks any stack trace information and other important properties
if(!productToAdd)
    throw ("How can I add new product when no value provided?");

Cita del blog: "Una cadena no es un error" (Del blog devthought, clasificado 6 para las palabras clave "objeto de error Node.JS")

"... pasar una cadena en lugar de un error da como resultado una interoperabilidad reducida entre los módulos. Rompe los contratos con las API que podrían estar ejecutando una instancia de las verificaciones de errores o que desean saber más sobre el error.. Los objetos de error, como veremos, tienen propiedades muy interesantes en los motores de JavaScript modernos, además de mantener el mensaje pasado al constructor ... "


Number3: Distinguir errores operacionales vs programadores

TL; DR: Los errores de operaciones (por ejemplo, la API recibió una entrada no válida) se refieren a casos conocidos en los que el impacto del error se comprende completamente y se pueden manejar con cuidado. Por otro lado, el error del programador (por ejemplo, tratar de leer la variable no definida) se refiere a fallas desconocidas del código que ordenan reiniciar correctamente la aplicación

De otra manera: Siempre puede reiniciar la aplicación cuando aparece un error, pero ¿por qué dejar ~ ~ 5000 usuarios en línea abajo debido a un error menor y predicho (error operacional)? lo contrario tampoco es ideal: mantener la aplicación activa cuando se produce un problema desconocido (error del programador) puede generar un comportamiento imprevisto. La diferenciación de los dos permite actuar con tacto y aplicar un enfoque equilibrado basado en el contexto dado

Ejemplo de código: hacerlo bien

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

Ejemplo de código: marcar un error como operativo (de confianza)

//marking an error object as operational 
var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;

//or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
function appError(commonType, description, isOperational) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.commonType = commonType;
    this.description = description;
    this.isOperational = isOperational;
};

throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);

//error handling code within middleware
process.on('uncaughtException', function(error) {
    if(!error.isOperational)
        process.exit(1);
});

Blog Quote: "De lo contrario, arriesgas el estado" (Del blog debugable, clasificado 3 para las palabras clave "Node.JS excepción no detectada")

"... Por la naturaleza misma de cómo funciona throw en JavaScript, casi nunca hay forma de "retomar de manera segura donde lo dejaste", sin referencias fugaces, o creando algún otro tipo de estado frágil indefinido. La forma más segura de responder a un error lanzado es cerrar el proceso. Por supuesto, en un servidor web normal, es posible que tenga muchas conexiones abiertas, y no es razonable abatirlas abruptamente porque alguien más activó un error. El mejor enfoque es enviar una respuesta de error a la solicitud que desencadenó el error, dejando que los demás terminen en su tiempo normal, y dejar de escuchar nuevas solicitudes en ese trabajador ".


Number4: maneja los errores centralmente, a través de, pero no dentro del middleware

TL; DR: El manejo de errores de la lógica, como el correo a la administración y el registro, debe encapsularse en un objeto dedicado y centralizado al que todos los puntos finales (por ejemplo, Express middleware, cron jobs, unit-testing) llamen cuando se presente un error.

De otra manera: Si no se manejan los errores dentro de un solo lugar, se duplicará el código y probablemente se manipularán incorrectamente.

Ejemplo de código: un flujo de error típico

//DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
    if (error)
        throw new Error("Great error explanation comes here", other useful parameters)
});

//API route code, we catch both sync and async errors and forward to the middleware
try {
    customerService.addNew(req.body).then(function (result) {
        res.status(200).json(result);
    }).catch((error) => {
        next(error)
    });
}
catch (error) {
    next(error);
}

//Error handling middleware, we delegate the handling to the centrzlied error handler
app.use(function (err, req, res, next) {
    errorHandler.handleError(err).then((isOperationalError) => {
        if (!isOperationalError)
            next(err);
    });
});

Presupuesto del blog: "A veces, los niveles más bajos no pueden hacer nada útil, excepto propagar el error a su interlocutor" (Del blog Joyent, clasificado 1 para las palabras clave "Node.JS error handling")

"... Puede terminar manejando el mismo error en varios niveles de la pila. Esto sucede cuando los niveles inferiores no pueden hacer nada útil, excepto propagar el error a su interlocutor, que propaga el error a su interlocutor, y así sucesivamente. solo la persona que llama más arriba sabe cuál es la respuesta adecuada, ya sea para volver a intentar la operación, informar un error al usuario u otra cosa. Pero eso no significa que deba intentar informar todos los errores a un solo nivel superior devolución de llamada, porque esa devolución de llamada no puede saber en qué contexto ocurrió el error "


Number5: errores de API de documento utilizando Swagger

TL; DR: Deje que los llamadores de su API sepan qué errores podrían presentarse a cambio para que puedan manejarlos cuidadosamente sin fallar. Esto se hace generalmente con marcos de documentación REST API como Swagger

De otra manera:Un cliente API puede decidir bloquearse y reiniciarse solo porque recibió un error que no pudo entender. Nota: la persona que llama de su API puede ser usted (muy típico en un entorno de microservicios)

Presupuesto del blog: "Tienes que decirles a tus interlocutores qué errores pueden suceder" (Del blog Joyent, clasificado 1 para las palabras clave "Node.JS logging")

... Hemos hablado sobre cómo manejar los errores, pero cuando está escribiendo una nueva función, ¿cómo se envían los errores al código que llamó a su función? ... Si no sabe qué errores pueden suceder o no sabe a qué se refieren, entonces su programa no puede ser correcto, excepto por accidente. Entonces, si estás escribiendo una nueva función, debes decirle a tus interlocutores qué errores pueden suceder y qué significan.


Number6: cierra el proceso con gracia cuando un extraño llega a la ciudad

TL; DR: Cuando se produce un error desconocido (error de desarrollador, consulte el número de práctica recomendada n. ° 3): existe incertidumbre acerca de la salubridad de la aplicación. Una práctica común sugiere reiniciar el proceso cuidadosamente usando una herramienta 'reiniciadora' como Forever y PM2.

De otra manera: Cuando se detecta una excepción desconocida, algunos objetos pueden estar en un estado defectuoso (por ejemplo, un emisor de eventos que se usa globalmente y que ya no se activan debido a fallas internas) y todas las solicitudes futuras pueden fallar o comportarse de manera loca

Ejemplo de código: decidir si se bloquea

//deciding whether to crash when an uncaught exception arrives
//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
 errorManagement.handler.handleError(error);
 if(!errorManagement.handler.isTrustedError(error))
 process.exit(1)
});


//centralized error handler encapsulates error-handling related logic 
function errorHandler(){
 this.handleError = function (error) {
 return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError);
 }

 this.isTrustedError = function(error)
 {
 return error.isOperational;
 }

Presupuesto del blog: "Hay tres escuelas de pensamiento sobre el manejo de errores" (Del blog jsrecipes)

... Existen principalmente tres escuelas de pensamiento sobre el manejo de errores: 1. Deje que la aplicación se bloquee y reiníciela. 2. Controle todos los errores posibles y nunca se cuelgue. 3. El enfoque equilibrado entre los dos


Número 7: utilice un registrador maduro para aumentar la visibilidad de los errores

TL; DR: Un conjunto de herramientas de registro maduras como Winston, Bunyan o Log4J acelerará el descubrimiento y la comprensión de errores. Así que olvídate de console.log.

De otra manera: Navegando por console.logs o manualmente a través de un archivo de texto desordenado sin consultar herramientas o un visualizador de registro decente puede mantenerte ocupado en el trabajo hasta tarde

Ejemplo de código: Winston Logger en acción

//your centralized logger object
var logger = new winston.Logger({
 level: 'info',
 transports: [
 new (winston.transports.Console)(),
 new (winston.transports.File)({ filename: 'somefile.log' })
 ]
 });

//custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });

Presupuesto del blog: "Permite identificar algunos requisitos (para un registrador):" (Del strongblog del blog)

... Permite identificar algunos requisitos (para un registrador):   1. Marca el tiempo en cada línea de registro. Este es bastante explicativo: debería poder decir cuándo se produjo cada entrada de registro.   2. El formato de registro debe ser fácilmente digerible tanto para humanos como para máquinas.   3. Permite múltiples flujos de destino configurables. Por ejemplo, es posible que esté escribiendo registros de seguimiento en un archivo, pero cuando se encuentre un error, escriba en el mismo archivo, luego en el archivo de error y envíe un correo electrónico al mismo tiempo ...


Número8: descubre errores y tiempo de inactividad usando productos APM

TL; DR: Los productos de monitoreo y rendimiento (a.k.a. APM) miden de forma proactiva su base de código o API para que puedan resaltar errores, bloqueos y ralentizar automáticamente partes que faltan.

De otra manera: Es posible que dedique un gran esfuerzo a medir el rendimiento y los tiempos de inactividad de la API, probablemente nunca sabrá cuáles son los componentes de código más lentos en el escenario del mundo real y cómo estos afectan a la experiencia del usuario.

Presupuesto del blog:"Segmentos de productos APM" (Del blog Yoni Goldberg)

"... los productos de APM constituyen 3 segmentos principales:1. Sitio web o monitoreo API - servicios externos que monitorean constantemente el tiempo de actividad y el rendimiento a través de solicitudes HTTP. Se puede configurar en pocos minutos. Los siguientes son pocos contendientes seleccionados: Pingdom, Uptime Robot y New Relic    2. Instrumentación del código - familia de productos que requieren incrustar un agente dentro de la aplicación para beneficiarse de la detección de código lento, estadísticas de excepciones, monitoreo de rendimiento y mucho más. Los siguientes son algunos contendientes seleccionados: New Relic, App Dynamics    3. Tablero de inteligencia operativa - Esta línea de productos se centra en facilitar al equipo de operaciones con métricas y contenido curado que ayuda a mantenerse al tanto del rendimiento de las aplicaciones. Esto generalmente implica agregar múltiples fuentes de información (registros de aplicaciones, registros de bases de datos, registros de servidores, etc.) y el trabajo de diseño del panel de control inicial. Los siguientes son pocos contendientes seleccionados: Datadog, Splunk "


Lo anterior es una versión abreviada - ver aquí más prácticas recomendadas y ejemplos


59
2017-09-05 20:11



Puede atrapar excepciones no detectadas, pero es de uso limitado. Ver http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit, forever o upstart se puede usar para reiniciar el proceso del nodo cuando falla. Un cierre correcto es lo mejor que puede esperar (por ejemplo, guardar todos los datos en memoria en controlador de excepciones no detectadas).


27
2018-04-10 19:45



dominios nodejs es la forma más actualizada de manejar errores en nodejs. Los dominios pueden capturar tanto eventos de error / otros como objetos lanzados tradicionalmente. Los dominios también proporcionan la funcionalidad para manejar las devoluciones de llamada con un error pasado como el primer argumento a través del método de interceptación.

Al igual que con el manejo de errores de tipo try / catch normal, generalmente es mejor lanzar errores cuando ocurren, y bloquear las áreas donde desea aislar los errores para que no afecten al resto del código. La forma de "bloquear" estas áreas es llamar a domain.run con una función como un bloque de código aislado.

En el código síncrono, lo anterior es suficiente: cuando ocurre un error, lo dejas pasar o lo atrapas y manejas allí, revertiendo cualquier dato que necesites revertir.

try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

Cuando el error ocurre en una devolución de llamada asíncrona, debe ser capaz de manejar completamente la reversión de datos (estado compartido, datos externos como bases de datos, etc.). O tiene que configurar algo para indicar que ha ocurrido una excepción: donde quiera que se preocupe por esa bandera, debe esperar a que se complete la devolución de llamada.

var err = null;
var d = require('domain').create();
d.on('error', function(e) {
  err = e;
  // any additional error handling
}
d.run(function() { Fiber(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(err != null) {
    // handle data reversion
    // probably log too
  }

})});

Parte del código anterior es feo, pero puedes crear patrones para que sea más bonito, por ejemplo:

var specialDomain = specialDomain(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(specialDomain.error()) {
    // handle data reversion
    // probably log too
  } 
}, function() { // "catch"
  // any additional error handling
});

ACTUALIZACIÓN (2013-09):

Arriba, uso un futuro que implica semántica de fibras, lo que le permite esperar en futuros en línea. Esto realmente le permite usar bloques tradicionales try-catch para todo - que considero que es la mejor manera de hacerlo. Sin embargo, no siempre se puede hacer esto (es decir, en el navegador) ...

También hay futuros que no requieren semántica de fibras (que luego funcionan con JavaScript normal y navegador). Estos pueden llamarse futuros, promesas o diferidos (a partir de ahora me referiré a los futuros). Las bibliotecas de futuros de JavaScript simple permiten que los errores se propaguen entre futuros. Solo algunas de estas bibliotecas permiten que cualquier futuro arrojado se maneje correctamente, así que ten cuidado.

Un ejemplo:

returnsAFuture().then(function() {
  console.log('1')
  return doSomething() // also returns a future

}).then(function() {
  console.log('2')
  throw Error("oops an error was thrown")

}).then(function() {
  console.log('3')

}).catch(function(exception) {
  console.log('handler')
  // handle the exception
}).done()

Esto imita un try-catch normal, aunque las piezas son asincrónicas. Imprimiría:

1
2
handler

Tenga en cuenta que no imprime '3' porque se lanzó una excepción que interrumpe ese flujo.

Eche un vistazo a las promesas de los azulejos:

Tenga en cuenta que no he encontrado muchas otras bibliotecas además de estas que manejan adecuadamente las excepciones lanzadas. El diferido de jQuery, por ejemplo, no lo hace: el manejador "fallido" nunca obtendría la excepción arrojada por un manejador 'then', que en mi opinión es un factor de ruptura.


15
2018-01-10 00:16



Escribí sobre esto recientemente en http://snmaynard.com/2012/12/21/node-error-handling/. Una nueva característica del nodo en la versión 0.8 es dominios y le permite combinar todas las formas de manejo de errores en una sola forma de administración. Puedes leer sobre ellos en mi publicación.

También puedes usar algo como Bugsnag para rastrear sus excepciones no detectadas y ser notificado por correo electrónico, sala de chat o tener un boleto creado para una excepción no detectada (soy el cofundador de Bugsnag).


11
2017-09-11 14:26



Una instancia en la que puede ser apropiado usar un try-catch es cuando se usa un ciclo forEach. Es sincrónico pero al mismo tiempo no se puede usar una declaración de devolución en el alcance interno. En cambio, se puede utilizar un enfoque try and catch para devolver un objeto Error en el ámbito apropiado. Considerar:

function processArray() {
    try { 
       [1, 2, 3].forEach(function() { throw new Error('exception'); }); 
    } catch (e) { 
       return e; 
    }
}

Es una combinación de los enfoques descritos por @balupton arriba.


4
2018-06-12 16:35



Me gustaría agregar que Biblioteca Step.js lo ayuda a manejar excepciones al pasarlo siempre a la siguiente función de paso. Por lo tanto, puede tener como último paso una función que verifique cualquier error en cualquiera de los pasos anteriores. Este enfoque puede simplificar enormemente el manejo de errores.

A continuación hay una cita de la página de github:

cualquier excepción lanzada se atrapa y pasa como el primer argumento para   la siguiente función. Siempre y cuando no anides las funciones de devolución de llamada en línea   sus principales funciones esto evita que haya alguna vez sin ser capturado   excepciones Esto es muy importante para servidores node.JS de larga ejecución   ya que una sola excepción no detectada puede hacer que todo el servidor caiga.

Además, puede usar Step para controlar la ejecución de scripts para tener una sección de limpieza como último paso. Por ejemplo, si desea escribir un script de compilación en Node e informar cuánto tiempo tomó escribir, el último paso puede hacer eso (en lugar de tratar de desenterrar la última devolución de llamada).


3
2017-09-10 04:15



Después de leer esta publicación hace algún tiempo me preguntaba si era seguro usar dominios para el manejo de excepciones en un nivel de API / función. Quería usarlos para simplificar el código de manejo de excepciones en cada función asíncrona que escribí. Mi preocupación era que el uso de un nuevo dominio para cada función introduciría una sobrecarga significativa. Mi tarea parece indicar que hay una sobrecarga mínima y que el rendimiento es realmente mejor con los dominios que con la captura de prueba en algunas situaciones.

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/


2
2017-09-06 13:01



Si desea usar Servicios en Ubuntu (Upstart): Nodo como servicio en Ubuntu 11.04 con upstart, monit y forever.js


1
2017-09-25 01:36



Los errores de captura se han discutido muy bien aquí, pero vale la pena recordar registrar los errores en alguna parte para que pueda verlos y arreglarlos.

Bunyan es un marco de registro popular para NodeJS: respalda la escritura de un conjunto de diferentes lugares de salida, lo que lo hace útil para la depuración local, siempre que evite console.log. 1. En el controlador de errores de tu dominio, puedes escupir el error en un archivo de registro.

var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

Esto puede llevar mucho tiempo si tiene que controlar muchos errores y / o servidores, por lo que podría valer la pena buscar en una herramienta como Raygun (exención de responsabilidad, trabajo en Raygun) para agrupar errores juntos o usarlos juntos. 1. Si decidiste usar Raygun como herramienta, también es bastante fácil de configurar

var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

1. Cruzado con el uso de una herramienta como PM2 o ​​para siempre, su aplicación debería poder bloquearse, desconectarse de lo que sucedió y reiniciarse sin ningún problema importante.


1