从回调中捕获异常 [英] Catching exceptions from callbacks

查看:67
本文介绍了从回调中捕获异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为Node.js使用本地MongoDB驱动程序,试图找到一种方法来捕获API回调中可能发生的异常.

I'm using the native MongoDB driver for Node.js and I'm trying to find a way to catch exceptions that may happen in the API's callbacks.

我知道最佳实践是将代码包装在try-catch块中,但是在我的特殊情况下,我向用户公开了驱动程序的API,因此我没有那种控制级别.尽管如此,我仍需要捕获回调中发生的潜在异常,以便发送回相关的错误消息.

I know that best practice would be to wrap the code in a try-catch block, but in my particular case I expose the driver's API to users so I don't have that level of control. Still, I need to catch potential exceptions happening in callbacks in order to send back relevant error messages.

进一步的解释. 想想一些后端即服务",用户可以在其中创建在节点中的服务器上运行的JS代码.用户代码可以直接从驱动程序访问db对象,并且可以使用它执行所需的任何操作,例如编写以下代码:

further explanations. Think of some "backend-as-a-service" where users can create JS code that is run on the server, in Node. The user code has direct access to a db object (from the driver) and can do whatever it wants with it, like writing the following:

db.collection('test').findOne({ field: 'value' }, function(err, doc){
  throw new Error;
});

我需要以某种方式抓住它,但是process.on('uncaughtException')对我来说太低了.该用户代码是从API请求执行的,因此我想在响应中返回相关的错误消息.

I need to somehow catch that but process.on('uncaughtException') is too low level for me. That user code is executed from an API request so I'd like to return the relevant error message in its response.

到目前为止,我已经尝试将错误处理程序(.on('error'))添加到db对象,但是没有捕获任何东西.任何帮助将不胜感激.

So far, I've tried to add an error handler (.on('error')) to the db object, but it doesn't catch anything. Any help would be greatly appreciated.

谢谢!

推荐答案

任何来自MongoDB驱动程序API的错误都会作为第一个参数传递给回调.将调用包装在try/catch中不会帮助您,因为错误是异步发生的.换句话说,对MongoDB API的所有调用都是开始一个异步操作,传递一个回调,以便在异步操作完成后(您的数据库调用返回)在以后调用.如果在异步操作过程中发生错误,您将无法通过try/catch捕获它.只有库本身才能捕获这些错误,这就是为什么库确保确保将它们作为第一个参数传递给您的回调的原因.

Any errors coming from the MongoDB driver API are passed to the callbacks as the first argument. Wrapping the calls in try/catch won't help you because the errors happen asynchronously. In other words, all your call to the MongoDB API does is begin an asynchronous operation, passing in a callback to be invoked later when the async operation is complete (your database call returns). If an error happens during the async operation, you won't be able to catch it with try/catch. Only the library itself will be able to catch those errors, which is why the library makes sure to pass them to your callbacks as the first argument.

collection.update({ a : 2 }, { $set: { b : 1 } }, function(err, result) {
  if (err) { /* do error handling */ return; }
  /* err is null, continue as normal */
});

如果我要编写自己的API来封装其中的某些调用,则可以使用以下三种方法之一进行操作.

If I were writing my own API that wraps some of these calls I'd do it one of three ways.

  1. 如果我要公开的API像MongoDB驱动程序一样是回调样式的API,那么我会做类似的事情.

  1. If the API I'm exposing is a callback-style API just like the MongoDB driver, then I'd do something like this.

exports.coolFunction = function (callback) {
  collection.update({ a : 2 }, { $set: { b : 1 } }, function (err, result) {
    if (err) { callback(err); return; }
    callback(null, result);
};

然后有人可以像这样使用我的API:

Then someone could consume my API like this:

var alexLib = require('alexLibExample');
alexLib.coolFunction(function (err, result) {
  if (err) { /* consumer handles error how they want to in their app */ }
  /* all is good, no errors, continue normally */
});

  • 或者我可以将我的API设为启用承诺的API.

  • Or I could make my API a promise-enabled API.

    var Promise = require('bluebird');
    exports.coolFunction = function () {
      return new Promise(function (resolve, reject) {
        collection.update({ a : 2 }, { $set: { b : 1 } }, function (err, result) {
          if (err) { reject(err); return; }
          resolve(result);
        });
      };
    };
    

    那么有人可以像这样使用我启用了promise的API:

    Then someone could consume my promise-enabled API like this:

    var alexLib = require('alexLibExample');
    alexLib.coolFunction().then(function (result) {
      /* do stuff with result */
    }).catch(function (err) {
      /* handle error how user chooses in their app */
    });
    

  • 或者我可以将错误作为错误事件发出,这似乎更像是您所追求的.

  • Or I could emit errors as an error event, which seems to be more what you're after.

    var EventEmitter = require('events').EventEmitter;
    exports.eventEmitter = new EventEmitter();
    exports.coolFunction = function (callback) {
      collection.update({ a : 2 }, { $set: { b : 1 } }, function (err, result) {
        if (err) { exports.eventEmitter.emit('error', err); return; }
        callback(result);
      });
    };
    

    然后有人可以像这样使用我的事件样式API:

    Then someone could consume my event-style API like this:

    var alexLib = require('alexLibExample');
    alexLib.eventEmitter.on('error', function (err) {
      /* user handles error how they choose */
    });
    alexLib.coolFunction(function (result) {
      /* do stuff with result */
    });
    

    API的事件样式通常与回调样式结合使用.这意味着它们仍然会将错误传递给回调函数,这是大多数用户在传递回调时所期望的.然后,它们还发出错误事件,作为用户可以订阅的一种全局错误处理程序.我知道这是猫鼬的工作方式.我可以捕获单个API调用中的错误,也可以设置一个错误事件处理程序并在那里处理所有错误.

    The event style of API is usually combined with the callback style. Meaning they still pass errors to the callback functions, which is what most users expect when passing in callbacks. Then they also emit an error event as a sort of global error handler the user can subscribe to. I know this is the way Mongoose works. I can catch the errors on the individual API calls or I can setup an error event handler and handle all errors there.

    var EventEmitter = require('events').EventEmitter;
    exports.eventEmitter = new EventEmitter();
    exports.coolFunction = function (callback) {
      collection.update({ a : 2 }, { $set: { b : 1 } }, function (err, result) {
        if (err) { exports.eventEmitter.emit('error', err); callback(err); return; }
        callback(null, result);
      });
    };
    

    然后,用户在处理错误方面具有一定的灵活性.

    Then the user has some flexibility with how they handle errors.

    var alexLib = require('alexLibExample');
    alexLib.eventEmitter.on('error', function (err) {
      /* handle all errors here */
    });
    alexLib.coolFunction(function (err, result) {
      if (err) { return; }
      /* do stuff with result */
    });
    alexLib.coolFunction2(function (err, result) {
      if (err) { /* maybe I have special error handling for this specific call. I can do that here */ }
      /* do stuff with result */
    });
    

  • 如果您真的想花哨的话,可以将这三种样式结合起来.

    If you really want to get fancy, you can combine all three styles.

    var EventEmitter = require('events').EventEmitter;
    var Promise = require('bluebird');
    exports.eventEmitter = new EventEmitter();
    exports.coolFunction = function (callback) {
      return new Promise(function (resolve, reject) {
        collection.update({ a : 2 }, { $set: { b : 1 } }, function(err, result) {
          if (err) {
            if (callback) { callback(err); }
            reject(err);
            exports.eventEmitter.emit('error', err);
          }
          if (callback) { callback(err, result); }
          resolve(result);
        });
      });
    };
    

    然后有人可以随便选择使用我的API.

    Then someone could consume my API however they choose.

    var alexLib = require('alexLibExample');
    alexLib.eventEmitter.on('error', function (err) {
      /* handle all errors here */
    });
    alexLib.coolFunction(function (err, result) {
      if (err) {
        /* handle this specific error how user chooses */
      }
      /* do stuff with result */
    });
    
    // or
    
    alexLib.coolFunction().then(function (result) {
      /* do stuff with result */
    }).catch(function (err) {
      /* handle any errors in this promise chain */
    });
    

    这篇关于从回调中捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆