未捕获从 mongoose-promise 回调函数抛出的错误

Posted

技术标签:

【中文标题】未捕获从 mongoose-promise 回调函数抛出的错误【英文标题】:Error thrown from a mongoose-promise callback function is not caught 【发布时间】:2015-06-29 21:42:14 【问题描述】:

我花了太多时间试图弄清楚为什么我的 express.js 控制器没有响应一个简单的查询,并发现 Mongoose-promise 回调引发的运行时错误正在默默地中断回调过程。

这是我的代码的简化版本:

server.get('/api/test', function (req, res, next) 
  User.find().exec().then(function success(users)
    console.log('SUCCESS');  
    typo[0] = 1; // throws a runtime error
    res.json(users);
  , function error(err)
    console.log('ERROR');  
    res.json(error: err);
  );
);

这导致SUCCESS 出现在我的控制台中,但随后什么也没有发生。没有给用户任何响应,我的拼写错误导致的错误没有出现在我的控制台中,也没有调用错误回调。

我知道不应从回调函数中抛出异常,但在这种情况下,这只是一个错字,对我来说,无论何时收到警告(例如,我的标准输出中的堆栈跟踪)都是有意义的犯这种错误。 (毕竟我们是人类……)

在您看来,当 Promise 回调中出现此类错误时,获得反馈的最佳方式是什么?

【问题讨论】:

这就是我将“投掷安全”称为“投掷危险”的原因。错误被承诺代码无形地捕获并“吞下”。幸运的是,错误也会传播。要使其可观察到,请尝试在 Promise 链的末尾添加 .catch(),并记录错误。 【参考方案1】:

这是 Mongoose 使用错误的 Promise 实现的错误。 Promise 是抛出安全的,因此异常会被捕获(因此它们可以在以后由未来的代码处理)——未来的代码永远不会出现,Mongoose 也不会报告它没有出现。良好的承诺实现不会受到这个问题的影响。

您的选择有两个:

使用像 Bluebird 这样的库:

var Promise = require("bluebird");
var mongoose = Promise.promisifyAll(require("mongoose"));

User.findAsync().then(function(data)
    JSON.prase("dsa"); // not a silent failure, will show up, easy debugging
);

这具有比猫鼬承诺更快的优势,因此没有性能损失。或者,如果您非常保守并且不希望获得 bluebird 的性能和 API 增益 - 您可以使用原生 Promise:

// Promise is the native promise
Promise.resolve(User.find().exec()).then(function(data)
    JSON.prase("dsa");
);

然后,假设您正在运行 nodejs 的现代变体(阅读:io.js v 1.4.1 或更高版本),您可以订阅承诺拒绝:

process.on("unhandledRejection", function(p, why)
    console.log("FOUND ERROR!!!!", p , why);
);

因此异常不会被静默抑制。

【讨论】:

感谢您的精彩回复,本杰明!太糟糕了,显然没有办法在不替换(例如findAsync() 而不是find())或包装(Promise.resolve())我所有的 Mongoose DB 调用的情况下使用另一个 Promise 实现...... @adrienjoly 如果它让你感觉好些我也在问题跟踪器上问过 :) 如果你想帮助打开一个问题,要求他们实施拒绝挂钩(这是一个规范),也许他们会是谁知道 这对我不起作用,直到我添加了以下内容:mongoose.set('error', true)。我的代码略有不同我使用Promise = require("bluebird") mongoose = require('mongoose') mongoose.Promise = PromisePromise.promisifyAll(require("mongoose")) 有区别吗【参考方案2】:

exec() 有两个承诺

.then(function) 
.then(null , function)

试试这个,我想会有帮助

server.get('/api/test', function(req, res, next) 
    User.find().exec()
        .then(function success(users) 
            console.log('SUCCESS');
            typo[0] = 1; // throws a runtime error
            res.json(users);
        )
        .then(null, function error(err) 
            console.log('ERROR');
            res.json(
                error: err
            );
        );
);

【讨论】:

不,它不起作用。 => TypeError: Object #<Promise> has no method 'catch' 对不起,我已经更新了我的答案,我在 mongoose 和 sequelize 的调用类型上发生了冲突 我相信exec().catch(handler)在promise的一般情况下类似于exec().then(null, handler) ... 并使用两个then() 相当于将成功和错误回调合并到同一个then() 中。 cf mongoosejs.com/docs/api.html#promise_Promise-then => 并没有解决问题。 区别在于.then(handler).then(null, handler).then(handler, handler2)

以上是关于未捕获从 mongoose-promise 回调函数抛出的错误的主要内容,如果未能解决你的问题,请参考以下文章

未捕获的引用错误:未定义回调

来自第 3 方静态库的回调中未捕获的异常

未捕获的 TypeError:回调不是 Vuejs 上的函数

从本机事件发射器回调后,React 本机 UI 未呈现。甚至回调有状态改变

未捕获的类型错误:无法在“窗口”上执行“requestAnimationFrame”:作为参数 1 提供的回调不是函数

forEach on map function js断言测试未运行