Nœud.js Meilleures Pratiques de gestion des exceptions

je viens de commencer à essayer node.js il y a quelques jours. J'ai réalisé que le noeud est terminé chaque fois que j'ai une exception dans mon programme. C'est différent du conteneur normal du serveur auquel j'ai été exposé où seul le Thread Worker meurt lorsque des exceptions non manipulées se produisent et que le conteneur serait toujours en mesure de recevoir la requête. Cela soulève quelques questions:

  • est-ce que process.on('uncaughtException') est le seul moyen efficace de s'en protéger?
  • Sera process.on('uncaughtException') attraper l'exception non gérée au cours de l'exécution de processus asynchrones?
  • y a-t-il un module qui est déjà construit (tel que l'envoi d'e-mails ou l'écriture à un fichier) que je pourrais utiliser dans le cas d'exceptions non récupérées?

je vous serais reconnaissant de toute pointeur/article que de me montrer le commun des meilleures pratiques pour gérer les exceptions non traitées dans le nœud.js

667
demandé sur Rakesh Sankar 2011-09-05 20:15:20

10 réponses

mise à jour: Joyent a maintenant leur propre guide mentionné dans cette réponse . L'information suivante est plus un résumé:

sécu-rité" lancer "erreurs

idéalement, nous aimerions éviter autant que possible les erreurs non récupérées, en tant que tel, au lieu de littéralement jeter l'erreur, nous pouvons plutôt en toute sécurité "jeter" l'erreur en utilisant l'une des méthodes suivantes en fonction de notre architecture de code:

  • pour le code synchrone, si une erreur se produit, retourner l'erreur:

    // 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)
    }
    
  • Pour rappel (ie. asynchrone), le premier argument de la callback est err , si une erreur se produit err est l'erreur, si une erreur ne se produit pas alors err est null . Tous les autres arguments suivent l'argument err :

    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)
        }
    })
    
  • For eventful code, où l'erreur peut se produire n'importe où, au lieu de jeter l'erreur, le feu le error événement à la place :

    // 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)
    

en toute sécurité "rattrapage" erreurs

parfois cependant, il peut encore y avoir un code qui jette une erreur quelque part qui peut conduire à une exception uncaught et un crash potentiel de notre application si nous ne pas l'attraper en toute sécurité. Selon notre architecture du code, nous pouvons utiliser l'une des méthodes suivantes pour l'attraper:

  • lorsque nous savons où l'erreur se produit, nous pouvons envelopper cette section dans un noeud .domaine 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 nous savons où l'erreur se produit est du code synchrone, et pour une raison quelconque ne peut pas utiliser les domaines( peut-être ancienne version de noeud), nous pouvons utilisez la déclaration 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)
    }
    

    cependant, attention à ne pas utiliser try...catch en code asynchrone, car une erreur lancée asynchrone ne sera pas attrapée:

    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
    }
    

    si vous voulez travailler avec try..catch en conjonction avec le code asynchrone, lors de L'exécution du noeud 7.4 ou supérieur, vous pouvez utiliser async/await nativement pour écrire vos fonctions asynchrones.

    autre chose à faire avec try...catch est le risque d'envelopper votre Rappel de fin de travaux à l'intérieur de la déclaration try comme ceci:

    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
    }
    

    Cette chasse aux sorcières est très facile à faire en tant que votre code devient de plus en plus complexe. En tant que tel, il est préférable d'utiliser des domaines ou de retourner des erreurs pour éviter (1) les exceptions non récupérées dans le code asynchrone (2) l'exécution de capture try que vous ne voulez pas. Dans les langages qui permettent un filetage approprié au lieu de JavaScript asynchrone événement-machine le style, c'est moins un problème.

  • enfin, dans le cas où une erreur non commise se produit dans un endroit qui n'a pas été enveloppé dans un domaine ou un essai de déclaration de capture, nous pouvons faire notre application ne se crash en utilisant le récepteur uncaughtException (cependant, ce faisant peut mettre l'application dans un unknown state ):

    // 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
    
656
répondu balupton 2018-09-29 13:13:39

ci-dessous est un résumé et une curation de nombreuses sources différentes sur ce sujet, y compris un exemple de code et des citations de billets de blog sélectionnés. La liste complète des meilleures pratiques peut être trouvée ici


les Meilleures pratiques de Nœud.JS erreur de manipulation


Numéro1: Utiliser des promesses pour async erreur de manipulation

TL;DR: Gestion asynchrone des erreurs dans le rappel de style est probablement le moyen le plus rapide de l'enfer (un.k.un la pyramide de doom). Le meilleur cadeau que vous pouvez donner à votre code est d'utiliser à la place une bibliothèque de promesses de bonne réputation qui fournit beaucoup de syntaxe de code compacte et familière comme try-catch

Sinon: Noeud.JS callback style, function( err, response), est une façon prometteuse de code non maintenable en raison de la combinaison de la manipulation d'erreur avec occasionnel code, de l'excès de nidification et maladroit modèles de codage

exemple de Code-bon

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

"1519160920 exemple de code" anti-pattern – rappel style de gestion d'erreur

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){ 
                    ...
                });
            });
        });
    });
});

citation de Blog:" nous avons un problème avec les promesses " (Du blog pouchdb, classé 11 pour les mots clés "Node Promises")

"...Et dans en fait, les callbacks font quelque chose d'encore plus sinistre: ils nous privent de la pile, ce que nous tenons généralement pour acquis dans les langages de programmation. Écrire du code sans pile, c'est comme conduire une voiture sans pédale de frein: on ne se rend pas compte à quel point on en a besoin, jusqu'à ce qu'on y arrive et qu'il n'y en ait plus. le but des promesses est de nous rendre les fondamentaux de la langue que nous avons perdus lorsque nous sommes allés à async: le retour, le jet et la pile. Mais vous devez savoir comment les utiliser promet correctement afin d'en tirer profit. "


numéro 2: N'utiliser que l'objet D'erreur incorporé

TL;DR: il est assez courant de voir un code qui envoie des erreurs sous forme de chaîne ou de type personnalisé – cela complique la logique de gestion des erreurs et l'interopérabilité entre les modules. Que vous rejetiez une promesse, que vous fassiez exception ou que vous émettiez une erreur – en utilisant le noeud.Js intégré Erreur de l'objet augmente l'uniformité et empêche la perte d'informations d'erreur

autrement: lors de l'exécution d'un module, être incertain quel type d'erreurs viennent en retour – rend beaucoup plus difficile de raisonner sur l'exception à venir et de le gérer. Même la peine, en utilisant des types personnalisés pour décrire les erreurs pourrait conduire à la perte de l'information d'erreur critique comme la trace de pile!

exemple de Code-le faire correctement

    //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?"));

exemple de code anti pattern

//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?");

citation de Blog:" une chaîne de caractères n'est pas une erreur " (Du blog devthought, classé 6 pour les mots clés " noeud.JS objet d'erreur")

" ...passer une chaîne de caractères au lieu d'une erreur réduit l'interopérabilité entre les modules. Il rompt des contrats avec des IPA qui pourraient être l'exécution instanceof contrôles d'Erreur, ou qui veulent en savoir plus sur l'erreur . Les objets d'erreur, comme nous le verrons, ont des propriétés très intéressantes dans les moteurs JavaScript modernes en plus de contenir le message transmis au constructeur.."


Number3: la Distinction opérationnelle vs programmeur erreurs

TL;DR: erreurs D'exploitation (p.ex. IPA reçu une entrée non valide) se référer à cas connus où l'impact de l'erreur est parfaitement compris et peut être manipulé de manière réfléchie. D'autre part, l'erreur de programmeur (par exemple essayer de lire la variable non définie) se réfère à des échecs de code inconnus qui dictent de redémarrer gracieusement l'application

sinon: vous pouvez toujours redémarrer l'application quand une erreur apparaît, mais pourquoi laisser tomber ~5000 utilisateurs en ligne en raison d'une erreur mineure et prédite (erreur opérationnelle)? l'inverse est également pas idéal – garder l'application vers le haut quand problème inconnu (erreur de programmeur) s'est produit pourrait conduire comportement imprévisible. Différencier les deux permet d'agir avec tact et d'appliquer une approche équilibrée basée sur le contexte donné

exemple de Code - il en train de faire

    //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?"));

exemple de code de marquage d'une erreur de l'exploitation (de confiance)

//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 citation : "sinon, vous risquez l'état" (Du blog debugable, classé 3 pour les mots-clés " noeud.Js uncaught exception")

...par la nature même de la façon dont throw fonctionne en JavaScript, il n'y a presque jamais aucun moyen de "ramasser en toute sécurité là où vous l'avez laissé", sans fuites de références, ou de créer une autre sorte d'état fragile non défini. La manière la plus sûre de répondre à une erreur lancée est d'arrêter le processus . De bien sûr, dans un serveur web normal, vous pourriez avoir de nombreuses connexions ouvertes, et il n'est pas raisonnable de les fermer brusquement parce qu'une erreur a été déclenchée par quelqu'un d'autre. La meilleure approche est d'envoyer une réponse d'erreur à la demande qui a déclenché l'erreur, tout en laissant les autres finir dans leur temps normal, et arrêter d'écouter de nouvelles demandes dans ce travailleur"


Number4: Gérer les erreurs de façon centralisée, en passant par, mais pas à l'intérieur middleware

TL;DR: la logique de traitement des erreurs telle que le courrier à l'administrateur et la journalisation doit être encapsulée dans un objet dédié et centralisé que tous les points finaux (par exemple, middleware Express, cron jobs, unit-testing) appellent lorsqu'une erreur se produit.

sinon: le fait de ne pas traiter les erreurs à l'intérieur d'un même endroit entraînera une duplication du code et probablement des erreurs qui seront traitées de façon inappropriée

"1519160920 exemple de Code" - une erreur typique de flux

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

"citation de Blog: " parfois, les niveaux inférieurs ne peuvent rien faire d'utile, sauf propager l'erreur à leur interlocuteur" (Du blog Joyent, classé 1 pour les mots clés " noeud.JS erreur de manipulation")

" ... vous pouvez finir par gérer la même erreur à plusieurs niveaux de la pile. Cela se produit quand les niveaux inférieurs ne peuvent rien faire utile, sauf propager l'erreur de leur appelant, lequel se propage l'erreur à son appelant, et ainsi de suite. Souvent, seul l'interlocuteur de haut niveau sait quelle est la réponse appropriée, que ce soit pour réessayer l'opération, signaler une erreur à l'utilisateur, ou autre chose. Mais cela ne signifie pas que vous devez essayer de signaler toutes les erreurs à un seul rappel de haut niveau, parce que ce rappel lui-même ne peut pas savoir dans quel contexte l'erreur s'est produite"


Number5: documenter les erreurs API en utilisant Swagger

TL;DR: faites savoir à vos appelants à L'API quelles erreurs pourraient être commises en retour afin qu'ils puissent les traiter de façon réfléchie sans s'écraser. Cela est généralement fait avec les cadres de documentation de L'API REST comme Swagger

sinon: un client API pourrait décider de se planter et de redémarrer uniquement parce qu'il a reçu en retour une erreur qu'il ne pouvait pas comprendre. Remarque: l' appelant de votre API pourrait être vous (très typique dans un environnement microservices)

citation de Blog: " vous devez dire à vos appelants quelles erreurs peuvent se produire" (Du blog Joyent, classé 1 pour les mots clés " noeud.JS enregistrement")

...nous avons parlé de la façon de traiter les erreurs, mais quand vous écrivez une nouvelle fonction, Comment transmettez-vous les erreurs au code qui a appelé votre fonction? ...Si vous ne savez pas quelles erreurs peuvent se produisent ou ne savent pas ce qu'ils signifient, alors votre programme ne peut pas être correct sauf par accident. Donc, si vous écrivez une nouvelle fonction, vous devez dire à vos appelants quelles erreurs peuvent se produire et ce qu'ils pensent.


Number6: fermez le processus avec grâce quand un étranger vient à la ville

TL; DR: Lorsqu'une erreur inconnue se produit (une erreur de développeur, voir le numéro de la meilleure pratique 3)- Il y a incertitude au sujet de la santé de l'application. Une pratique courante suggère de redémarrer le processus soigneusement en utilisant un outil de "redémarrage" comme Forever et PM2

autrement: Lorsqu'une exception non familière est prise, un objet peut être dans un état défectueux (E. g un émetteur d'événements qui est utilisé globalement et qui ne déclenche plus d'événements en raison d'une défaillance interne) et toutes les demandes futures pourraient échouer ou se comporter de manière folle

Exemple de Code-décider s'il faut planter

//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;
 }

citation de Blog: "Il ya trois écoles de pensées sur le traitement des erreurs" (Du blog jsrecipes)

... il y a principalement trois écoles de pensées sur le traitement des erreurs: 1. Laissez l'application se planter et redémarrez-la. 2. Gérer toutes les erreurs possibles et ne jamais planter. 3. Approche équilibrée entre les deux


Number7: Utilisation de la maturité de l'enregistreur à l'augmentation des erreurs de visibilité

TL;DR: un ensemble d'outils de journalisation mûrs comme Winston, Bunyan ou Log4J, accélérera la découverte et la compréhension des erreurs. Alors oublie la console.journal.

sinon: Skimming through console.logs ou manuellement à travers un fichier texte messy sans outils d'interrogation ou un décent log viewer peut vous tenir occupé au travail jusqu'à la fin de

exemple de Code-Winston logger en action

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

citation de Blog: " permet d'identifier quelques exigences (pour un logger):" (Tiré du blog strongblog)

... permet d'identifier quelques exigences (pour un logger): 1. Horodatage de chaque ligne de journal. Celui-ci est assez explicite, vous devriez être capable pour dire quand chaque entrée de journal a eu lieu. 2. Le format de journalisation devrait être facilement digestible par les humains aussi bien que par les machines. 3. Permet de configurer plusieurs flux de destination. Par exemple, vous pourriez écrire des journaux de trace à un fichier, mais quand une erreur est rencontrée, écrire dans le même fichier, puis dans le fichier d'erreur et envoyer un e-mail en même temps...


Number8: Découvrez des erreurs et des pannes à l'aide de produits APM

TL;DR: Monitoring and performance products (A. K. a APM) de mesurer de manière proactive votre base de codes ou API afin qu'ils puissent automatiquement mettre en évidence par magie les erreurs, les accidents et les pièces lentes qui vous manquaient

sinon: vous pourriez dépenser beaucoup d'efforts pour mesurer les performances de L'API et les temps d'arrêt, probablement vous ne serez jamais au courant quelles sont vos parties de code les plus lentes dans le scénario du monde réel et comment cela affecte les UX

citation de Blog: " segments de produits APM" (Du blog Yoni Goldberg)

"...les produits APM constitue 3 grands secteurs: 1. Site web ou API de surveillance - services externes qui surveillent en permanence la disponibilité et les performances via des requêtes HTTP. Peut être installé en quelques minutes. Voici quelques prétendants sélectionnés: Pingdom, Robot Uptime, et nouvelle relique 2. Instrumentation de Code – famille de produits qui nécessitent d'intégrer un agent dans l'application pour bénéficier caractéristique détection de code lente, les statistiques d'exceptions, la surveillance du rendement et beaucoup plus. Suivants sont quelques prétendants: Nouvelle Relique, Application Dynamique 3. Operational intelligence dashboard - cette gamme de produits est axée sur la facilitation de l'équipe ops avec des mesures et du contenu curated qui aide à rester facilement à la pointe de la performance de l'application. Ce n'est généralement comprend l'agrégation de plusieurs sources d'information (journaux d'applications, journaux de bases de données, journaux de serveurs, etc.) et le travail initial de conception de tableaux de bord. Voici quelques candidats sélectionnés: Datadog, Splunk "


ce qui précède est une version abrégée - voir plus de pratiques exemplaires et d'exemples

62
répondu Yonatan 2016-04-18 20:21:04

vous pouvez attraper des exceptions non cachées, mais c'est d'usage limité. Voir http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit , forever ou upstart peut être utilisé pour redémarrer le processus de noeud quand il se brise. Un arrêt gracieux est le mieux que vous pouvez espérer (par exemple, enregistrer toutes les données en mémoire dans le handler d'exception uncaught).

27
répondu nponeccop 2011-09-10 22:13:56

NodeJS domains est le moyen le plus à jour de gérer les erreurs dans nodejs. Les domaines peuvent capturer à la fois l'erreur/d'autres événements ainsi que des objets lancés traditionnellement. Les domaines fournissent également une fonctionnalité pour traiter les callbacks avec une erreur passée comme premier argument via la méthode d'interception.

comme avec la manipulation d'erreurs try/catch-style normale, il est généralement préférable de jeter les erreurs quand elles se produisent, et de bloquer les zones où vous voulez isoler erreurs affectant le reste du code. La façon de "bloquer" ces zones est d'appeler domaine.courir avec une fonction comme un bloc isolé du code.

en code synchrone, ce qui précède est suffisant - quand une erreur se produit, vous la laissez passer, ou vous l'attrapez et manipulez là, en inversant toutes les données que vous avez besoin de revenir en arrière.

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

Lorsque l'erreur se produit dans un rappel asynchrone, vous devez être en mesure de gérer complètement l' retour de données (état partagé, données externes comme des bases de données, etc.). OU vous devez mettre quelque chose pour indiquer qu'une exception s'est passé - où jamais vous vous souciez de ce drapeau, vous devez attendre pour l'exécution du rappel.

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
  }

})});

une partie de ce code ci-dessus est laid, mais vous pouvez créer des modèles pour vous-même pour le rendre plus joli, par exemple:

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

mise à JOUR (2013-09):

ci-dessus , j'utilise un futur qui implique fibres sémantiques , qui vous permettent d'attendre sur les futurs en ligne. Cela vous permet d'utiliser les blocs try-catch pour tout - que je trouve la meilleure façon d'aller. Cependant, on ne peut pas toujours le faire (c'est à dire dans le navigateur)...

il y a aussi des futures qui ne nécessitent pas de sémantique des fibres (qui fonctionnent alors avec le JavaScript normal, browsery). Ceux-ci peuvent être appelés futures, promesses, ou reports (je vais juste faire référence aux futures à partir de maintenant). Les bibliothèques à terme JavaScript permettent de propager des erreurs entre les différents contrats à terme. Seulement quelques-unes de ces bibliothèques permettent à n'importe quel futur jeté d'être correctement manipulé, alors méfiez-vous.

un exemple:

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()

cela imite une prise d'essai normale, même si les pièces sont asynchrones.

1
2
handler

notez qu'il n'affiche pas '3' parce qu'une exception a été lancée qui interrompt ce flux.

regardez les promesses de bluebird:

notez que je n'ai pas trouvé beaucoup d'autres bibliothèques que celles qui gèrent correctement les exceptions lancées. jQuery est différé, par exemple, ne le faites pas - le handler "fail" n'obtiendrait jamais l'exception lancé un handler "then", qui à mon avis, est un briseur d'affaire.

15
répondu B T 2015-07-02 22:36:36

j'ai écrit à ce sujet récemment à http://snmaynard.com/2012/12/21/node-error-handling / . Une nouvelle fonctionnalité de node dans la version 0.8 est domains et vous permet de combiner toutes les formes de gestion des erreurs dans un formulaire de gestion plus facile. Vous pouvez lire à leur sujet dans mon post.

vous pouvez également utiliser quelque chose comme Bugsnag pour suivre vos exceptions uncaught et être notifié par e-mail, chatroom ou avoir un billet créé pour un uncaught exception (je suis le co-fondateur de Bugsnag).

11
répondu Simon Maynard 2013-01-10 00:16:11

l'utilisation d'une boucle forEach est l'une des situations où il peut être approprié d'utiliser un try-catch. Il est synchrone mais en même temps vous ne pouvez pas simplement utiliser une instruction de retour dans la portée intérieure. Au lieu d'essayer et attraper approche peut être utilisée pour renvoyer un objet d'Erreur dans le champ d'application approprié. Considérons:

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

il s'agit d'une combinaison des approches décrites par @balupton ci-dessus.

4
répondu Michael Yagudaev 2012-09-11 14:26:39

je voudrais juste ajouter que .la bibliothèque js vous aide à gérer les exceptions en la passant toujours à la fonction next step. Par conséquent, vous pouvez avoir comme dernière étape une fonction qui vérifie les erreurs dans l'une des étapes précédentes. Cette approche peut grandement simplifier votre gestion des erreurs.

ci-dessous est une citation de la page github:

toutes les exceptions lancées sont attrapées et passées comme premier argument de la fonction suivante. Aussi longtemps que vous ne nichez pas les fonctions de rappel en ligne vos fonctions principales ceci empêche qu'il n'y ait jamais de panne exception. Ceci est très important pour le noeud de longue durée.Serveurs JS depuis une seule exception non récupérée peut faire tomber le serveur entier.

de plus, vous pouvez utiliser Step pour contrôler l'exécution des scripts pour avoir une section de nettoyage comme dernière étape. Par exemple, si vous voulez écrire un script de construction dans Node et rapport combien de temps il a fallu pour l'écrire, la dernière étape peut le faire (plutôt que d'essayer de creuser le dernier rappel).

3
répondu Michael Yagudaev 2012-06-12 17:47:14

après avoir lu ce post il y a quelque temps, je me demandais s'il était sécuritaire d'utiliser des domaines pour la gestion des exceptions au niveau de l'api / fonction. Je voulais les utiliser pour simplifier le code de gestion des exceptions dans chaque fonction asynchrone que j'ai écrite. Ma préoccupation était que l'utilisation d'un nouveau domaine pour chaque fonction introduirait des frais généraux importants. Mes devoirs semblent indiquer qu'il ya un minimum de frais généraux et que la performance est en fait meilleure avec les domaines qu'avec try catch dans certaines situations.

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

2
répondu Sudsy 2013-09-10 04:15:57

si vous voulez utiliser les Services dans Ubuntu(Upstart): noeud comme un service dans Ubuntu 11.04 avec upstart, monit et pour toujours.js

1
répondu Cap 2013-03-19 13:22:53

attraper les erreurs a été très bien discuté ici, mais il est intéressant de se rappeler de noter les erreurs quelque part afin que vous puissiez les voir et réparer les choses.

Bunyan est un framework de journalisation populaire pour NodeJS-il supporte l'écriture à un tas de différents endroits de sortie ce qui le rend utile pour le débogage local, aussi longtemps que vous évitez la console.journal. ​ Dans le gestionnaire d'erreurs de votre domaine, vous pouvez recracher l'erreur dans un fichier journal.

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

cela peut prendre du temps si vous avez beaucoup d'erreurs et/ou de serveurs à vérifier, il pourrait donc être intéressant de regarder dans un outil comme Raygun (disclaimer, je travaille chez Raygun) pour grouper les erreurs ensemble - ou les utiliser tous les deux ensemble. ​ Si vous avez décidé d'utiliser Raygun comme un outil, il est assez facile de configurer trop

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

​ Croisé avec l'utilisation d'un outil comme PM2 ou pour toujours, votre application devrait être en mesure de se planter, déconnecter ce qui s'est passé et redémarrer sans problèmes majeurs.

1
répondu K. Craven 2015-09-29 19:51:58