title: Why is it possible to try-catch an async-await call?
body: <p>There is a common anti pattern in JavaScript:</p>
  
  <pre><code>function handleDataClb(err, data) {
      if(!data) throw new Error('no data found');
      // handle data... 
  } 
  
  function f() {
      try {
          fs.readFile('data', 'utf8', handleDataClb);
      } catch(e) {
          // handle error... 
      }
  }
  </code></pre>
  
  <p>This try-catch in <code>f</code> will not catch errors in the <code>handleDataClb</code> as the callback is called in a later stage and context where the try-catch is not visible anymore.</p>
  
  <p>Now in JavaScript async-await is implemented using generators, promises, and coroutines, as in:</p>
  
  <pre><code>// coroutine example 
  co(function* doTask() {
      try {
          const res1 = yield asyncTask1(); // returns promise
          const res2 = yield asyncTask2(); // returns promise
          return res1 + res2;
      } catch(e) {
          // handle error... 
      }
  });
  
  // async-await example
  async function doTask() {
      try {
          const res1 = await asyncTask1(); // returns promise
          const res2 = await asyncTask2(); // returns promise
          return res1 + res2;
      } catch(e) {
          // handle error... 
      }
  }
  </code></pre>
  
  <p>This way the try-catch works, which is often mentioned as one great advantage of async-await over callbacks.</p>
  
  <p>Why and how does the <code>catch</code> work? How does the coroutine aka <code>async</code> manage to throw the error inside the try-catch when one of the <code>asyncTask</code> calls results in a promise rejection?</p>
  
  <p>EDIT: as others have pointed out, the way how the JavaScript engine implements the <code>await</code> operator can be very different from the pure JavaScript implementation used by transpilers like Babel and shown above as <code>coroutine example</code>. Therefore to be more specific: how is this working using native JavaScript? </p>