【问题标题】:JS Promises: Allowing Propagation of ErrorsJS 承诺:允许错误传播
【发布时间】:2015-12-23 19:50:32
【问题描述】:

背景

假设我正在使用 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 函数是什么,请参阅下面的 cmets。

【问题讨论】:

  • 您期望它命中的“通用错误处理程序”是什么?如果controller 是一条路线,您应该使用第三个next 参数并执行next(error)
  • 我想你明白我在做什么——next(error) 听起来是正确的答案
  • 如果你展示你是如何调用controller()的,我们会更好地了解你在做什么以及哪些选项最有意义。
  • 如果 app 是一个 Express 应用程序,通用错误处理程序将简单地类似于 app.use(function(err, req, res, next) { ... })controller 函数是路由的处理程序,例如/
  • 您希望哪个通用错误处理程序受到重新抛出的错误的影响?没有 - 除了抱怨未处理的拒绝的那个。

标签: javascript express promise bluebird


【解决方案1】:

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

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

事实上,在您的 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() 处理程序被调用。在该处理程序中,您再次 throw 会将承诺保留为被拒绝的承诺。因此,以find().then(...) 开头的原始承诺现在将被拒绝。但是,没有更多的拒绝处理程序,您也没有从controller() 返回承诺。因此,此时您有一个未处理的拒绝承诺。这通常是一个编码错误。

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

  1. 您可以在.catch() 处理程序中自己处理错误,方法是调用您将错误传递给的某种错误处理函数,然后将res 参数传递给然后不要抛出错误。

  2. 您可以从 controller() 函数返回承诺,然后调用该函数的任何代码都可以在那里处理被拒绝的承诺。

【讨论】:

  • 您肯定会因为解释我收到错误的原因而获得支持。由于另一个答案提供了特定于 Express 的解决方案,因此我将不得不将其标记为解决方案。
【解决方案2】:

假设你已经用类似的东西绑定了你的函数

app.get('/', controller);

当 Express 调用 controller 时,它已让您 100% 控制。如果从controller 同步抛出异常,Express 很好,也会将其视为错误。但是,一旦您调用任何异步代码,您就有责任决定如何处理任何错误。

对于 Express,您有两种选择:

  1. 由于您已通过reqres,您可以捕获错误并将您想要的任何响应发送回用户。
  2. controller 实际上在 Express 中有 function(req, res, next) 的函数签名。这是一种非常常见的格式。

如果您没有在响应中写入任何内容,则应该调用next 回调。如果你在没有参数的情况下调用next(),这会告诉 Express 继续处理它拥有的一组 URL 处理程序,尝试找到一个处理请求的处理程序,如果没有找到则返回 404。

但是,如果您将参数传递给next,例如next(err),Express 将跳过剩余的 URL 处理程序,而不是寻找错误处理程序。 Express 允许您注册自定义处理程序,但如果没有找到,它将返回 500。

那么你应该在你的例子中做什么?你可能想要类似的东西

function controller (req, res, next) {
    find().then(function (result)) {
        if (!result) throw new SpecificError("Couldn't find it.");

        res.send("It exists!");
    }).catch(next);
}

这意味着如果在 Promise 链中抛出任何异常,next 函数将被调用,Express 将从那里接管。

【讨论】:

  • 是的,我认为这是我对如何使用 reqresnext 的误解。
猜你喜欢
  • 2018-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多