Browsers now have native support for doing asyncronous calls via / . This is nice. It is essentially syntax support for promises. async await Unfortunately, the error handling story for all this not so nice. Real footage of the async error delegation mechanism To summarize, here’s what we’ve got to deal with: 2 non-equivalent ways throwing errors. 2 mostly equivalent ways of catching local errors. From 0 to 1 ways of catching promise errors globally. 1 way to throw errors that don’t get caught by any of these mechanisms. Ugh. How did we end up with this? Well, when adding new features to a system, if every feature number n has to interact with all of the existing n-1 features, you get an O(n²) growth in feature-feature interactions. So for a linear growth in features, you get a quadratic growth in complexity. This actually explains why most big software projects fail, and why disentangling features is so important. It’s also what has happened to async error handling. We started with simple callback functions. Then it turned out that callbacks was a mess, so we fixed all that with Promises. Then it turned out that Promises was a mess, so we fixed all that with async/await. So let’s dig into the current mess. Handling errors in promises locally Promises, they break before they’re madeSometimes, sometimes - The Strokes, in a post to the WHATWG mailing list Thrown errors When an error is thrown in an async function, you can catch it with a . So this works as you'd expect: try {} catch {} () {throws Error();} async function fails () { { fails();} (e) {console.log("that failed", e);}} async function myFunc try await catch This is syntax sugar for what you might have been doing with promises earlier: fails().catch(e => {console.log("That also failed", e);}); In fact, anywhere you use the keyword , you can remove and do the traditional and calls. This is because the keyword implicitly creates a for its function. await await .then() .catch() async Promise The only difference between these two is that the callback for has its own execution context, i.e. variable scope works like you'd expect it to. catch() Rejected promises With Promises, it turns out you have another way of throwing errors, other than using throw. You can call : reject() () { Promise((resolve, reject) => {reject( Error());});} function fails2 return new new () { { fails2();} (e) {console.log("that failed", e);}} async function myFunc2 try await catch Errors passed to can be caught with both and with the method. So you have two ways to throw errors, and two ways to catch errors. This is more complex than we'd like, but at least both way of catching errors will catch either way of throwing them, so the complexity here isn't fully as bad as it could have been. reject() try {} catch {} .catch() Errors thrown in a different call stack There’s more trouble to be had though. If you’re creating yourself, chances are you're using either a or a , or in some way calling a callback function when some operation is done. These callbacks will be called from a different call stack, which means that thrown errors will propagate to somewhere that is not your code. Promise setTimeout() setInterval() Consider this example: () { Promise((resolve, reject) => {setTimeout( () { Error();}, 100);});} function fails3 return new function throw new () { { fails3();} (e) {console.log("that failed", e); //<-- never gets called}} async function myFunc3 try await catch The error produced here is never caught by the , because it is thrown on a different call stack. Using the method would have the same problem. try {} catch {} .catch(() => {}) The way to have an error propagate across such callbacks is to use the reject() function, like so: () { Promise((resolve, reject) => {setTimeout( () {reject( Error());}, 100);});} function fails4 return new function new () { { fails4();} (e) {console.log("that failed", e); //<-- this gets called}} async function myFunc4 try await catch This is presumably the main reason why the reject/resolve paradigm was introduced in the first place. Sidenote: Why reject/resolve kind of sucks. Calling in a promise is much like doing , except for a major difference: It's just a function call, so it doesn't break the execution flow like does. This means you can write paradoxical code that both s and s, like this: reject(new Error()) throw Error() throw reject resolve () { Promise((resolve, reject) => {reject( Error());resolve("great success");});} function schrödinger return new new Here both and will be called. So which will win? The answer is whichever function was called . reject() resolve() first Now look at this weirdo: () { Promise((resolve, reject) => { resolve("huh"); //<-- this throw is executed});} () { { schrödinger2();} (e) {console.log("caught error", e);//<-- yet, this isn't reached}} function schrödinger2 return new throw async function callAsync try await catch Here the promise has a single line of code, a throw statement. Yet, the is never triggered. This is because resolve was called, and the rule still is that whatever was called is what wins. So the throw is executed, but it is silently swallowed by the runtime. This is bound to cause endless confusion. try {} catch {} first These problems happen because and are near duplicates of and . I'll claim that the only reason we have reject/resolve is to be able to move errors across call stack boundaries. But it's a mediocre fix for that, for several reasons. It only moves the errors you expect, so e.g. an unexpected NullReferenceException will not be moved across boundaries unless you explicitly call with it yourself. Also, the fact that it duplicates core language features causes problems, as seen above. resolve() reject() return throw reject() There’s a cleaner design for this. C# has had / since before people started talking about it in . There, exceptions thrown in the async callbacks are caught, and then rethrown such that they propagate to the site that is awaiting the async operation. JavaScript could implement this by providing substitutes for setTimeout and setInterval with new semantics for errors, and we could ditch this / stuff in favor of / . This would also cut down the Promises spec by 90%. async await JavaScript resolve reject return throw Handling errors in promises globally So we know how to catch errors with and similar mechanisms. What about when you want to set up a global catch-all handler for all unhandled errors, for example to log these errors to a server? try {} catch {} Well, how do you even tell if an error in a promise is unhandled? When dealing with promises, you have no way of knowing if an error will be handled some time in the . The promise might call , and some code might come along 10 minutes later and call on that promise, in which case the error will be handled. For this reason, the global error handler in Promise libraries like Q and Bluebird has been named , which is a fitting name. In native Promises, this function is called , but they still can only tell if a rejection has been unhandled . future reject() .catch(() => {}) onPossiblyUnhandledRejection onunhandledrejection so far You can set up your global handler like this: window.onunhandledrejection = (evt) { /*Your code*/ } function or: window.addEventListener("unhandledrejection", (evt) { }) function Here is an object of type . is the promise that was rejected, and holds whatever object was passed to the function. evt PromiseRejectionEvent evt.promise evt.reason reject() This is all nice and dandy, except for this: No one except Chrome implement it (well, Chrome, and Chromium based browsers). It is coming to Firefox, and presumably to Safari and Edge as well. But not yet. To make matters worse, there is no good work around for these browsers, other than not using native Promises, and relying on a library like Q or Bluebird instead. Hopefully native support will arrive for these browsers soon. Tracking errors in promises How do you track JavaScript errors that happen on your site? For this you can use , which is a JavaScript error tracking service. It instruments the browser with a global error handler, which then logs any uncaught errors that occur. Deployment is simply done by dropping in a script file. CatchJS <script src="//cdn.catchjs.com/catch.js"></script> With this, uncaught errors get logged, along with various telemetry, which can include screenshots and click trails. Because of the ambiguity of whether or not a Promise rejection will be handled in the future, CatchJS does not attach it self to the onunhandledrejection handler. If you want this, you can set up such forwarding manually. window.onunhandledrejection = (evt) {console.error(evt.reason);} function CatchJS will instrument console.error, so these errors will be logged to your remote persistent log, as well as to the developers console. Mahatma Gandhi said that a breach of promise is a base surrender of truth. Then again, he knew nothing about async error handling. Originally published at catchjs.com .