title: How to handle synchronous errors with async await?
body: <p>I've been getting some unhandled rejections in my app, but I was pretty certain that all of my code is correctly wrapped with error handling. After digging around I found that async/await is not behaving as I expected. Basically my async function throws a sync error and that error is then getting treated as an uncaught exception. This happens even when I wrap my code in a try catch explicitly, but not when I await a sync method which throws an error.</p>
  
  <p>So here's my test code:</p>
  
  <pre><code>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 =&gt; console.log({ error }));
  }
  
  process.on('unhandledRejection', (e) =&gt; console.log('not caught', e.stack));
  
  test();
  </code></pre>
  
  <p>Here's the output I see in the console:</p>
  
  <pre><code>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]
  </code></pre>
  
  <p><strong>So my question is, is this expected behavior?</strong></p>
  
  <p>If so, how can I nicely wrap my async functions so that they catch both synchronous and asynchronous errors in a way that allows me to handle them my self or pass them on as rejections.</p>
  
  <p>I'm using babel to compile my code (this particular bit of code runs in node though I also run my code in the browser).</p>
  
  <p>If it's interesting, here's the compiled version of my code:</p>
  
  <pre><code>                                                                                                              //
  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();  
  </code></pre>
  
  <p><strong>Update</strong></p>
  
  <p>I can't reproduce this issue using the latest version of regenerator so assuming this is a bug in my version of regenerator or babel or something related to my environment.</p>
  
  <p><strong>Update</strong></p>
  
  <p>Lance Whatley points out that it's incorrect to return a promise from an async function, I'm not sure if that's the case, however the behavior that I'm definitely not expecting is that the console log statement inside of the <code>three()</code> catch clause is never called.</p>