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"))