如何使用异步等待处理同步错误?

时间:2017-12-07 17:41:07

标签: node.js error-handling async-await try-catch generator

我在我的应用程序中得到了一些未处理的拒绝,但我非常确定我的所有代码都正确包装了错误处理。在挖掘之后,我发现async / await不像我预期的那样。基本上我的异步函数抛出同步错误,然后该错误被视为未捕获的异常。即使我明确地将我的代码包装在try catch中,但是当我等待抛出错误的同步方法时,也会发生这种情况。

所以这是我的测试代码:

function test() {
  async function one() {
    try {
      await three();
    } catch (error) {
      console.log('caught', new Error().stack);
      return error;
    }
  }
  async function three() {
    try {
      throw new Error();
    } catch (e) {
      console.log('caught', new Error().stack);
      return Promise.reject(e);
    }
  }

  one().then(function (result) {
    console.log({result});
  }).catch(error => console.log({ error }));
}

process.on('unhandledRejection', (e) => console.log('not caught', e.stack));

test();

这是我在控制台中看到的输出:

not caught Error
    at three$ (imports/access-control/execute-handler.js:25:13)
    at tryCatch (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:65:40)
    at GeneratorFunctionPrototype.invoke [as _invoke] (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:299:22)
    at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:117:21)
    at tryCatch (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:65:40)
    at invoke (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:155:20)
    at /Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:198:11
    at callInvokeWithMethodAndArg (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:197:16)
    at AsyncIterator.enqueue (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:220:13)
    at AsyncIterator.prototype.(anonymous function) [as next] (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:117:21)
caught Error
    at one$ (imports/access-control/execute-handler.js:11:29)
    at tryCatch (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:65:40)
    at GeneratorFunctionPrototype.invoke [as _invoke] (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:299:22)
    at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:117:21)
    at tryCatch (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:65:40)
    at invoke (/Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:155:20)
    at /Users/joshuaohlman/Development/Xolvio/xspecs/modules/web-app/node_modules/regenerator-runtime/runtime.js:167:13
    at /Users/joshuaohlman/.meteor/packages/promise/.0.8.9.ghfed++os+web.browser+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:32:39
{ result: [Error] }
(STDERR) [Error] [Function]

所以我的问题是,这是预期的行为吗?

如果是这样,我怎样才能很好地包装我的异步函数,以便它们能够以一种允许我自己处理它们或者作为拒绝传递它们的方式捕获同步和异步错误。

我使用babel编译我的代码(虽然我也在浏览器中运行代码,但这个特定的代码在节点中运行。)

如果它很有趣,这里是我的代码的编译版本:

                                                                                                              //
function test() {                                                                                                      // 6
  function one() {                                                                                                     // 7
    return _regenerator2.default.async(function () {                                                                   // 7
      function one$(_context) {                                                                                        // 7
        while (1) {                                                                                                    // 7
          switch (_context.prev = _context.next) {                                                                     // 7
            case 0:                                                                                                    // 7
              _context.prev = 0;                                                                                       // 7
              _context.next = 3;                                                                                       // 7
              return _regenerator2.default.awrap(three());                                                             // 7
                                                                                                                      //
            case 3:                                                                                                    // 7
              _context.next = 9;                                                                                       // 7
              break;                                                                                                   // 7
                                                                                                                      //
            case 5:                                                                                                    // 7
              _context.prev = 5;                                                                                       // 7
              _context.t0 = _context["catch"](0);                                                                      // 7
              console.log('caught', new Error().stack);                                                                // 11
              return _context.abrupt("return", _context.t0);                                                           // 7
                                                                                                                      //
            case 9:                                                                                                    // 7
            case "end":                                                                                                // 7
              return _context.stop();                                                                                  // 7
          }                                                                                                            // 7
        }                                                                                                              // 7
      }                                                                                                                // 7
                                                                                                                      //
      return one$;                                                                                                     // 7
    }(), null, this, [[0, 5]]);                                                                                        // 7
  }                                                                                                                    // 7
                                                                                                                      //
  function two() {                                                                                                     // 15
    return _regenerator2.default.async(function () {                                                                   // 15
      function two$(_context2) {                                                                                       // 15
        while (1) {                                                                                                    // 15
          switch (_context2.prev = _context2.next) {                                                                   // 15
            case 0:                                                                                                    // 15
              _context2.prev = 0;                                                                                      // 15
              _context2.next = 3;                                                                                      // 15
              return _regenerator2.default.awrap(three());                                                             // 15
                                                                                                                      //
            case 3:                                                                                                    // 15
              _context2.next = 9;                                                                                      // 15
              break;                                                                                                   // 15
                                                                                                                      //
            case 5:                                                                                                    // 15
              _context2.prev = 5;                                                                                      // 15
              _context2.t0 = _context2["catch"](0);                                                                    // 15
              console.log('caught', new Error().stack);                                                                // 19
              return _context2.abrupt("return", _context2.t0);                                                         // 15
                                                                                                                      //
            case 9:                                                                                                    // 15
            case "end":                                                                                                // 15
              return _context2.stop();                                                                                 // 15
          }                                                                                                            // 15
        }                                                                                                              // 15
      }                                                                                                                // 15
                                                                                                                      //
      return two$;                                                                                                     // 15
    }(), null, this, [[0, 5]]);                                                                                        // 15
  }                                                                                                                    // 15
                                                                                                                      //
  function three() {                                                                                                   // 23
    return _regenerator2.default.async(function () {                                                                   // 23
      function three$(_context3) {                                                                                     // 23
        while (1) {                                                                                                    // 23
          switch (_context3.prev = _context3.next) {                                                                   // 23
            case 0:                                                                                                    // 23
              _context3.prev = 0;                                                                                      // 23
              throw new Error();                                                                                       // 23
                                                                                                                      //
            case 4:                                                                                                    // 23
              _context3.prev = 4;                                                                                      // 23
              _context3.t0 = _context3["catch"](0);                                                                    // 23
              console.log('caught', new Error().stack);                                                                // 27
              return _context3.abrupt("return", Promise.reject(_context3.t0));                                         // 23
                                                                                                                      //
            case 8:                                                                                                    // 23
            case "end":                                                                                                // 23
              return _context3.stop();                                                                                 // 23
          }                                                                                                            // 23
        }                                                                                                              // 23
      }                                                                                                                // 23
                                                                                                                      //
      return three$;                                                                                                   // 23
    }(), null, this, [[0, 4]]);                                                                                        // 23
  }                                                                                                                    // 23
                                                                                                                      //
  one().then(function (result) {                                                                                       // 32
    console.log({                                                                                                      // 33
      result: result                                                                                                   // 33
    });                                                                                                                // 33
  }).catch(function (error) {                                                                                          // 34
    return console.log({                                                                                               // 34
      error: error                                                                                                     // 34
    });                                                                                                                // 34
  });                                                                                                                  // 34
}                                                                                                                      // 35
                                                                                                                      //
process.on('unhandledRejection', function (e) {                                                                        // 37
  return console.log('not caught', e.stack);                                                                           // 37
});                                                                                                                    // 37
test();  

更新

我无法使用最新版本的再生器重现此问题,因此假设这是我的版本的再生器或babel或我的环境相关的错误。

更新

Lance Whatley指出从异步函数返回一个承诺是不正确的,我不确定是不是这样,但是我的行为绝对不是期望永远不会调用three() catch子句中的控制台日志语句。

2 个答案:

答案 0 :(得分:1)

问题出在three()异步功能中。你可以在你的catch块中使用return Promise.reject(e)而不是在三个函数中使用try / catch,并且让任何调用方法处理错误,或者你必须在three()中使用try / catch(for例如,如果要在将错误发送到调用函数之前记录您当前正在执行的输出,请在记录后再次抛出相同的异常。

重新定义three()以此方式运行的示例如下:仅更改catch块中的最后一行:

// Rejects this async function, so the calling function will act as if the promise was rejected
async function three() {
  try {
    throw new Error();
  } catch (e) {
    console.log('caught', new Error().stack);
    throw e;
  }
}

// Defines a function that returns a promise that's instantly rejected, so your calling method can handle the rejection
async function three() {
  throw new Error()
} 

最后一个throw e;有效地拒绝了你的调用函数中的promise,它具有预期的行为。运行return Promise.reject(e)不会拒绝调用异步函数,而是返回一个Promise拒绝对象,就像在父函数中解析它一样。

答案 1 :(得分:0)

原来这是一个错误,更新我的包版本修复了它。我会把这个问题留下来回答,因为我花了很长时间才发现问题并修复它,并且围绕使用try / catch块的async / await的正确行为存在很多混淆。