Node.js 如何使用async/await正确处理错误

Node.js 如何使用async/await正确处理错误,node.js,express,asynchronous,error-handling,promise,Node.js,Express,Asynchronous,Error Handling,Promise,我正在编写一个API,在这里我在错误处理方面遇到了一些问题。我不确定的是第一个代码段是否足够,或者是否应该像第二个代码段那样将其与承诺混合。任何帮助都将不胜感激 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(

我正在编写一个API,在这里我在错误处理方面遇到了一些问题。我不确定的是第一个代码段是否足够,或者是否应该像第二个代码段那样将其与承诺混合。任何帮助都将不胜感激

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);
    }
答案是:视情况而定

捕捉每一个错误

如果你想对每一个错误做出不同的反应,这是有意义的。 e、 g:

但是,代码可能会变得非常混乱,您可能希望以不同的方式拆分错误处理(例如:在某个预请求挂钩上执行JWT验证,只允许对处理程序执行有效请求,和/或在服务中执行
findById
save
部分,并在每个操作中抛出一次)

如果找不到具有给定ID的实体,可能需要抛出404

一网打尽

如果您希望在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});

我读过一些文章,其中建议每个请求都需要一个try/catch块。这是真的吗

不,这不是必需的
try/catch
with
await
在概念上类似于
try/catch
在常规同步异常下工作。如果您只想在一个位置处理所有错误,并且希望所有代码只中止到一个错误处理程序,而不管错误发生在何处,并且不需要捕获一个特定错误,这样您就可以对该特定错误执行特殊操作,那么您只需要一个
try/catch

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

或者,如果我像第二段代码那样将其与承诺混合在一起

这句话的措辞表明,您可能不太了解
wait
的情况,因为承诺涉及到您的两个代码块

在两个代码块中
models.user.findById(decoded.userId)返回一个承诺。你有两种方法可以实现这个承诺

  • 您可以使用
    wait
    来“暂停”函数的内部执行,直到该承诺得到解决或拒绝为止

  • 您可以使用
    .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
    function someFunc() {
        return someFunc()
          .catch(err => myDefaultValue)
          .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()
    处理程序链接您的承诺:

    // 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
    function someFunc() {
        return someFunc()
          .catch(err => myDefaultValue)
          .then(someFunc2)
          .catch(e => "");
    }
    

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

    第一个看起来很好。我读了一些文章,建议需要尝试/每个请求的catch块..这是否属实?这取决于情况。如果是:是否希望在每次异步调用后做出不同的响应?(例如,每个错误情况下都有不同的响应)否如果:仅当所有异步调用都已解决时,才继续进行响应?