Why is try {} .. catch() not working with async/await function?

async function errorTest() { /* ... */ }

try { 
  errorTest()
}
catch(err) { 
  console.log("OUTSIDE ERROR!" + err)
}

Because errorTest is async, it will always return a promise and it is never guaranteed to finish execution before the next statement begins: it is asynchronous. errorTest returns, and you exit the try block, very likely before errorTest is fully run. Therefore, your catch block will never fire, because nothing in errorTest would synchronously throw an exception.

Promise rejection and exceptions are two different channels of failure: promise rejection is asynchronous, and exceptions are synchronous. async will kindly convert synchronous exceptions (throw) to asynchronous exceptions (promise rejection), but otherwise these are two entirely different systems.

(I’d previously written that async functions do not begin to run immediately, which was my mistake: As on MDN, async functions do start to run immediately but pause at the first await point, but their thrown errors are converted to promise rejections even if they do happen immediately.)

function errorTest() {
  return new Promise(/* ... */);  // nothing throws!
}

function errorTestSynchronous() {
  throw new Error(/* ... */);     // always throws synchronously
}

function errorTestMixed() {
  // throws synchronously 50% of the time, rejects 50% of the time,
  // and annoys developers 100% of the time
  if (Math.random() < 0.5) throw new Error();
  return new Promise((resolve, reject) => { reject(); });
}

Here you can see various forms of throwing. The first, errorTest, is exactly equivalent to yours: an async function works as though you’ve refactored your code into a new Promise. The second, errorTestSynchronous, throws synchronously: it would trigger your catch block, but because it’s synchronous, you’ve lost your chance to react to other asynchronous actions like your $.get call. Finally, errorTestMixed can fail both ways: It can throw, or it can reject the promise.

Since all synchronous errors can be made asynchronous, and all asynchronous code should have .catch() promise chaining for errors anyway, it’s rare to need both types of error in the same function and it is usually better style to always use asynchronous errors for async or Promise-returning functions—even if those come via a throw statement in an async function.


As in Ayotunde Ajayi’s answer, you can solve this by using await to convert your asynchronous error to appear synchronously, since await will unwrap a Promise failure back into a thrown exception:

// within an async function
try { 
  await errorTest()
}
catch(err) { 
   console.log("OUTSIDE ERROR!" + err)
}

But behind the scenes, it will appear exactly as you suggested in your question:

errorTest()
    .then(val=> console.log(val))
    .catch(err=> console.error("ERROR OCCURRED"))

Leave a Comment