JS承诺:允许传播错误

时间:2015-09-25 22:08:44

标签: javascript express promise bluebird

背景

假设我正在使用NodeJS + Express。我在Express上注册了一些错误处理程序,它们将以适当的方式处理我的应用程序中可能出现的所有错误。

因此,每当我需要时,我会在应用程序中抛出错误。如果存在未处理的错误,我会让它传播直到它到达错误处理程序。但是,当在promise链中尝试抛出错误时,我遇到了一个问题。请看以下示例:

function find() {
    // consider this to be a promise from a library such as Bluebird
    return new Promise(function (resolve, reject) {
        // ... logic ...
    });
}

function controller (req, res) {

    // ... omitted ...

    find().then(function (result)) {
        if (result) {
            // let 'res' be the Express response object
            res.send("It exists!");
        } else {
            // let SpecificError be a prototypical subclass of Error
            throw new SpecificError("Couldn't find it.");
        }
    }).catch(function (error) {
        // throw the error again, so that the error handler can finish
        // the job
        throw error;
    });
}

虽然我一直在期待我重新投掷的错误,最终至少击中了一般错误处理程序,但我却看到我发送给我的应用程序的请求挂起,而我正在使用的promise库抱怨Unhandled rejection

问题

很简单,我想知道如何通过在我的承诺链中抛出错误来解决我似乎错误处理我正在创建的拒绝这一事实。

编辑:有关错误处理程序和controller函数的具体内容的说明,请参阅下面的注释。

2 个答案:

答案 0 :(得分:3)

假设您已将功能与

绑定
controller

当快递拨打controller时,它已向您提供100%的控制权。如果从req同步抛出异常,Express很好,并且会将此视为错误。但是,只要您调用任何异步代码,就有责任决定如何处理任何错误。

对于Express,您有两种选择:

  1. 由于您已通过rescontroller,因此您可以发现错误并将您想要的任何回复发送给用户。
  2. function(req, res, next)在Express中实际上具有next的函数签名。这是一种非常常见的格式。
  3. 如果你的没有在响应中写了任何东西,那么next()回调期望被调用。如果你在没有参数的情况下调用next,这会告诉Express继续处理它拥有的URL处理程序集,试图找到一个处理请求的处理程序,如果没有找到则返回404.

    但是,如果您将参数传递给next(err) function controller (req, res, next) { find().then(function (result)) { if (!result) throw new SpecificError("Couldn't find it."); res.send("It exists!"); }).catch(next); } ,则Express会跳过剩余的URL处理程序,而不是查找错误处理程序。 Express允许您注册自定义处理程序,但如果没有找到,则返回500。

    那么你的例子应该怎么做?你可能想要像

    这样的东西
    next

    这意味着如果在promise链中抛出任何异常,将使用它调用$comande = new Commande(); 函数,Express将从那里接管。

答案 1 :(得分:1)

承诺处理程序是“安全的”。这意味着您在任何promise处理程序中抛出的任何异常都将被自动捕获并变为被拒绝的promise。这就是规范是为promises编写的,以及它们是如何工作的(除了某些版本的jQuery承诺,但这只是因为它们没有遵循规范)。

所以,如果你从你的诺言库得到“未处理的拒绝”,那么这是一个有用的警告,告诉你你有一个被拒绝的承诺没有处理程序,所以拒绝被忽略,这通常是编码错误。

事实上,在你的controller()函数中,你完全具有这个:

function controller (req, res) {

    // ... omitted ...

    find().then(function (result)) {
        if (result) {
            // let 'res' be the Express response object
            res.send("It exists!");
        } else {
            // let SpecificError be a prototypical subclass of Error
            throw new SpecificError("Couldn't find it.");
        }
    }).catch(function (error) {
        // throw the error again, so that the error handler can finish
        // the job
        throw error;
    });
}

如果你到达throw new SpecificError所在的行,那么这将使承诺成为被拒绝的承诺。这将导致您的.catch()处理程序被调用。在那个处理程序中,你再次抛出,这将承诺作为被拒绝的承诺。因此,以find().then(...)开头的原始承诺现在将成为被拒绝的承诺。但是,没有更多拒绝处理程序,并且您没有从controller()返回承诺。所以,那时你有一个未经处理的被拒绝的承诺。这通常是编码错误。

如何更正此编码错误,您有多种选择:

  1. 您可以通过调用某种错误处理函数在.catch()处理程序中自己处理错误,该函数将错误传递给您并将res参数传递给抛出错误。

  2. 您可以从controller()函数返回承诺,无论代码是什么,该函数都可以处理被拒绝的承诺。