【问题标题】:How to gracefully handle promise rejection in express如何优雅地处理 express 中的 promise 拒绝
【发布时间】:2019-08-25 12:12:24
【问题描述】:

我有以下快递控制器

class ThingsController {

  static async index(req, res, next) {
    try {
      const things = await Thing.all();
      res.json(things);
    } catch(err) {
      next(err);
    }  
  }
}

和路由器

router.route('/things').get(ThingsController.index)

在我的应用程序中,我计划有几个控制器使用 Promise 来呈现结果

我不想每次都重复 try/catch 块

我的第一个解决方案是将这个逻辑提取到处理承诺拒绝函数中:

const handlePromiseRejection = (handler) =>

  async (req, res, next) => {
    try{
      await handler(req, res, next);
    } catch(err) {
      next(err);
    };
  };

现在我们可以从 ThingsController.index 中删除 try/catch 块,并且需要将路由器更改为:

router.route('/things')
  .get(handlePromiseRejection(ThingsController.index))

但在每条路线上添加handlePromiseRejection 可能是一项繁琐的任务,我希望有更聪明的解决方案。

你有什么想法吗?

【问题讨论】:

  • 看看express-async-errors(可能有人和你有同样的想法)
  • 它就像@naga-elixir-jar 的魅力。您能否将此评论变成答案,我会接受它

标签: express promise es6-promise express-router


【解决方案1】:

在路由中使用async/await 处理错误的正常方法是捕获错误并将其传递给catch all 错误处理程序:

app.use(async (req, res) => {
  try {
    const user = await someAction();
  } catch (err) {
    // pass to error handler
    next(err)
  }
});

app.use((err, req, res, next) => {
  // handle error here
  console.error(err);
});

使用express-async-errors 包,您可以简单地使用throw(或者不用担心某些函数会抛出error)。来自文档:Instead of patching all methods on an express Router, it wraps the Layer#handle property in one place, leaving all the rest of the express guts intact.

用法很简单:

require('express-async-errors'); // just require!
app.use(async (req, res) => {
  const user = await User.findByToken(req.get('authorization')); // could possibly throw error, implicitly does catch and next(err) for you

  // throw some error and let it be implicitly handled !!
  if (!user) throw Error("access denied");
});

app.use((err, req, res, next) => {
  // handle error
  console.error(err);
});

【讨论】:

  • 实际上它对我有用,无需手动抛出错误。我有测试检查应用程序在拒绝承诺时回复 500 并且它们通过而无需在处理程序中引发错误。 Express 有开箱即用的错误处理程序,如果发生错误,它会以 500 响应
  • if (!user) throw Error("access denied"); 如果你指的是这一行,作者的意思是你可以在你的路线中抛出错误而不是做next(error),包隐含地为你做这件事。只是一个你可以抛出错误的例子。
【解决方案2】:

所以,如果你真的想处理每条路线,这就是你处理承诺拒绝的方式。

这也是它的一个单行 ES6 版本。

代码

const handlePromiseRejection = (handler) => (req, res, next) => handler(req, res, next).catch(next)

尽管如你所问,最简单的方法是在 index.js 或 app.js 上使用unhandledRejection 收听进程

process.on("unhandledRejection", (error, promise) => {
  console.log("Unhandled Rejection at:", promise, "reason:", reason);
  // Application specific logging, throwing an error, or other logic here
});

来自Node.js

【讨论】:

  • handler(req, res, next).catch(next) 版本的 handlePromiseRejection 在处理程序不是异步函数的情况下将不起作用(如果我们在应用程序中的每个路由处理程序上应用 handlePromiseRejection
  • 是的,但 OP 询问如何使用通用方法处理 Promise 拒绝。
  • 您的解决方案的问题是,当我请求 API 端点时,我的浏览器永远挂起,并且由于未处理的承诺拒绝而失败
猜你喜欢
  • 2017-06-02
  • 2021-06-11
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 2016-11-10
  • 2015-04-26
  • 2018-08-26
  • 1970-01-01
相关资源
最近更新 更多