如何使用异步/等待进行正确的错误处理

时间:2018-09-09 13:04:53

标签: node.js express asynchronous error-handling promise

我正在编写一个API,其中我在错误处理方面遇到了一些麻烦。我不确定的是第一个代码段是否足够,或者是否应该像第二个代码段那样将它与promise混合使用。任何帮助将非常感激!

try {
  var decoded = jwt.verify(req.params.token, config.keys.secret);
  var user = await models.user.findById(decoded.userId);
  user.active = true;
  await user.save();
  res.status(201).json({user, 'stuff': decoded.jti});
} catch (error) {
  next(error);
}

第二个代码段:

try {
      var decoded = jwt.verify(req.params.token, config.keys.secret);
      var user = models.user.findById(decoded.userId).then(() => {

      }).catch((error) => {

      });
      user.active = true;
      await user.save().then(() => {

      }).catch((error) => {

      })
      res.status(201).json({user, 'stuff': decoded.jti});
    } catch (error) {
      next(error);
    }

2 个答案:

答案 0 :(得分:0)

答案是:这取决于

捕获每个错误

使您理解是否希望对每个错误进行不同的反应。 例如:

 try {
        let decoded;
        try {
          decoded = jwt.verify(req.params.token, config.keys.secret);
        } catch (error) {
          return response
            .status(401)
            .json({ error: 'Unauthorized..' });
        }
...

但是,代码可能会变得很混乱,并且您希望对错误进行一些不同的拆分(例如:对某些 pre request 钩子进行JWT验证,并仅允许有效请求处理程序和/或在服务中执行findByIdsave部分,并在每次操作中抛出一次。

如果未找到具有给定ID的实体,则可能要抛出404。

一次捕获全部

如果您想以 a 的方式做出反应,则 a) b) c)错误,那么第一个示例就很好。

  a) var decoded = jwt.verify(req.params.token, config.keys.secret);
  b) var user = await models.user.findById(decoded.userId);
  user.active = true;
  c) await user.save();
  res.status(201).json({user, 'stuff': decoded.jti});

答案 1 :(得分:0)

  

我阅读了一些文章,建议对每个请求都需要一个try / catch块。这有什么道理吗?

否,不是必需的。 try/catchawait在概念上的工作方式类似于try/catch在常规同步异常下的工作方式。如果您只想在一个地方处理所有错误,并且希望所有代码都在一个错误处理程序中中止,无论错误发生在什么地方,也不需要捕获一个特定错误,那么您可以针对该特定错误执行一些特殊的操作,那么您只需要一个try/catch

但是,如果您需要专门处理一个特定的错误,甚至可能允许其余代码继续运行,那么您可能需要一个更本地的错误处理程序,可以是本地try/catch或{{ 1}}上返回承诺的本地异步操作。

  

或者我是否应该像第二个代码片段中那样将它与promises混合使用。

此措辞表明您可能不太了解.catch()的情况,因为在两个代码块中都包含了保证。

在您的两个代码块中,await返回一个Promise。您有两种使用诺言的方法。

  1. 您可以将models.user.findById(decoded.userId);await一起使用,以“暂停”该函数的内部执行,直到承诺得以解决或拒绝为止。

  2. 您可以使用.then().catch()查看承诺何时解决或拒绝。

两者都使用models.user.findById(decoded.userId);函数调用中的承诺返回。因此,您最好这样说:“或者,如果我应该在特定的承诺上使用本地.catch()处理程序,而不是将所有拒绝都放在一个地方。


这样做:

// skip second async operation if there's an error in the first one
async function someFunc() {

    try {
        let a = await someFunc():
        let b = await someFunc2(a);
        return b + something;
    } catch(e) {
        return "";
    }
}

类似于最后将您的诺言与一个.catch()处理程序链接:

// skip second async operation if there's an error in the first one
function someFunc() {
    return someFunc().then(someFunc2).catch(e => "");
}

无论哪个异步函数拒绝,都将应用相同的错误处理程序。如果第一个拒绝,则第二个不执行,因为流程直接进入错误处理程序。如果在第一个异步操作中出现错误,您就是希望流通过的方式,这就是很好的条件。

但是,假设您希望将第一个函数中的错误转换为默认值,以便始终执行第二个异步操作。然后,这种控制流程将无法实现。相反,您必须在源头捕获第一个错误,以便提供默认值并继续执行第二个异步操作:

// always run second async operation, supply default value if error in the first
async function someFunc() {

    let a;
    try {
        a = await someFunc():
    } catch(e) {
        a = myDefaultValue;
    }
    try {
        let b = await someFunc2(a);
        return b + something;
    } catch(e) {
        return "";
    }
}

类似于最后将您的诺言与一个.catch()处理程序链接:

// always run second async operation, supply default value if error in the first
function someFunc() {
    return someFunc()
      .catch(err => myDefaultValue)
      .then(someFunc2)
      .catch(e => "");
}

注意:此示例从不拒绝someFunc()返回的承诺,而是提供默认值(此示例中为空字符串),而不是拒绝向您展示此函数中处理错误的不同方式。这当然不是必需的。在许多情况下,仅返回被拒绝的承诺是正确的选择,然后呼叫者可以决定如何处理拒绝错误。