异常未被捕获,直到事件监听器被激活

Exception goes uncaught till a event listener is activated

我正在使用的 module 正在抛出一个异常,该异常未被捕获。这是片段:

try {
    mandrill_client.templates.info({ name: 'plaintexttemplates' }, (r, e) => {

      console.log(e, 'e');


      console.log(r, 'r');
    });
} catch (e) {

    console.log('From trycatch', e); // NEVER CAUGHT!!
}

现在,当我 运行 这样做时,会发生以下情况:

/home/suhail/test/node_modules/mandrill-api/mandrill.js:114
  throw {
  ^
Unknown_Template: No such template "plaintexttemplates"

这确实是一个未处理的异常(来自我正在使用的节点模块)。但是为什么我的 try/catch 无法捕捉到这个?

为了捕捉这个异常,我激活了一个监听器:

process.on('uncaughtException', (err) => {
    console.log('ERRRR', err); // WORKS FINE, OBVIOUSLY!!
});

看来我的概念对异常处理不正确。谁能告诉我为什么 try/catch 无法捕获异常?我错过了什么吗?

我没有在这里发布模块代码,但是已经发布了对下面代码函数的引用。这是我在调试时看到的流程控制。 以下是调用 mandrill_client.templates.info.

时调用的后续方法

第一个调用的方法是: Templates.prototype.info

控制传递给 Mandrill.prototype.call

最后是抛出这个错误的方法:Mandrill.prototype.onerror

PS:根据 documentation: throw 语句抛出用户定义的异常。当前函数的执行会停止(throw之后的语句不会执行),control will be passed to the first catch block in the call stack。如果调用函数之间不存在 catch 块,程序将终止。

但这并没有发生!

原因是您在 try...catch 块中执行的函数使用回调处理错误,并且可能在调用回调之前执行一些异步任务。 Javascript try...catch 仅适用于 synchronous/blocking 个任务。

如果库异步抛出,那就是个问题。除了编辑库之外,您无法通过任何其他方式解决该问题。糟糕的图书馆,糟糕的图书馆。

异步函数启动异步操作,然后 returns 您的代码继续执行,直到调用堆栈清除(并且您的所有异常处理程序都消失了)。然后,稍后,异步操作触发回调(带有干净的调用堆栈),如果它在那里抛出异常,调用堆栈中有 none 的代码来捕获它。库有责任捕获自己的异步异常并以记录的方式将它们传回给调用者——通常通过回调中的错误参数(就像你在标准 nodejs 库中看到的所有异步操作一样)。

举个例子来说明:

function delay(t, callback) {
   setTimeout(function() {
       throw new Error("try to catch me");
       callback();    // won't get here
   }, t);
}

try {
   delay(100, function() {
       console.log("won't get here");
   });
} catch(e) {
   console.log("won't find any exception here");
}

console.log("will get here before the exception is thrown");

// exception will be thrown sometime later with a clean callstack so
// the above try/catch is no longer in place

处理这个问题的正确方法是让异步操作捕获它自己的异常并通过回调将它们反馈回来:

function delay(t, callback) {
   setTimeout(function() {
       // async operation catches its own exceptions
       try {
           throw new Error("try to catch me");
           callback(null);    // won't get here
       } catch(e) {
           callback(e);
       }
   }, t);
}

delay(100, function(err) {
    if (err) {
        console.log(err);
    } else {
        console.log("got here with no error");
    }
});

console.log("will get here before the exception is thrown");