JavaScript Error Handling

JavaScript Async/Await Exception Handling with Airbrake!

Introduced in ES6, promises are the big leap forward into asynchronous operations that JavaScript has needed for some time. However, in ES7 (or ESNext, as the upcoming release is sometimes referred to), promises were dramatically improved with the introduction of async functions and the await operator. In short, an async function simply defines and returns an asynchronous function, while the await operator is used to wait for a Promise object.

The secret sauce when using async and await together is that they effectively allow code to be written synchronously, while their behavior is still carried out behind the scenes as an asynchronous fashion. This is an extremely powerful feature, but the most noticeable effect when implementing async/await into your code is just how simple and easy to maintain the new syntax becomes.

In this article we’ll explore how to manage JavaScript async/await exception handling, including easy integration of the Airbrake-JS library, so let’s get to it!

Full Code Sample

Below is the full code sample we’ll be using in this example. Feel free to copy and paste it into your own project to try it out or follow along:

The Setup

For our example code we’ll be using the basic Book class:

It has a handful of properties with explicit getters and setters (using the get and set keywords), but otherwise, the only special logic is the specific set(property, value) and get(property) methods. These are generalized methods which make it easier to create Promises for our example. In particular, notice that the set(property, value) method returns a new Promise(...) object. To simulate an asynchronous action we’re explicitly calling setTimeout() and delaying by one second. Since we’re returning a Promise, we call the resolve(...) method to indicate a successful call, while we invoke reject(...) when the call has failed. In this case, reject(...) is called only when attempting to update the author property while an author value already exists. This behavior could simulate business logic that requires rejecting a data record update when a value already exists, but, since it’s an IO action, it must be performed asynchronously.

Using Promises

We’ll begin with the most basic implementation of asynchronous operations, which is a plain old Promise object. As we saw above, the Book.set(property, value) method returns a Promise object, so our testPromise() function below creates a new Book instance, sets the title using the set(property, value) method, then invokes the .then method of the returned Promise object, which is called once the Promise comes back with a result:

Since setting the title property always succeeds, that will work and output the Promise result to the log. However, the attempt to set a new author value will fail, since an author already exists. This is confirmed by the log output, which occurs after the artificial one second delay:

Using Promises with Airbrake

Let’s integrate our Promise asynchronous methodology with the Airbrake-JS module. We won’t go over the setup process of Airbrake-JS here, but be sure to check out the official documentation for information on installing the library.

Once Airbrake-JS is installed and we’ve created a new project on the Airbrake dashboard, we can include the library in whatever manner is easiest. For this example, we’re using requirejs, so we start by defining the location of our app within the app.js file, along with the location of the Airbrake-JS module:

Now, within our main application code we require Airbrake-JS, and then we can use the Airbrake client object to set our projectId and projectKey, both of which are found on the Airbrake dashboard:

As you can see, we’ve opted to pass the Airbrake client object as a parameter to the testPromiseWithAirbrake(airbrake) function, but we could just as easily instantiate it inside the function if we wish. Here we see the code of the aforementioned function:

In may look a bit complicated, but very little is different from our previous testPromise() function. All we’ve added is the call to airbrake.notify(err). Since the notify(...) method returns a Promise object itself, we next call the then(...) method on that object to perform various actions, depending if the Promise succeeded or failed.

Executing the function above produces the following output, indicating that the attempt to set a new author failed again, but this time we caught the error and processed it via Airbrake, which generated a new notification on the Airbrake dashboard:

Sure enough, opening the Airbrake dashboard and looking at the Errors panel shows a new error report, with an Error Message of "Cannot update Author from Stephen King to Await w/ Airbrake Author.". Neat!

Using Async/Await

Now, let’s see what happens if we update our original testPromise() function to use async/await functionality. Here’s the testAsyncAwait() function to do just that:

What should immediately jump out at you is just how little code is required. This function performs the same logic as the testPromise() function, but requires nearly half the number of characters and lines of code. Moreover, there’s no more need to mess with the complication of then(...) method callbacks. Instead, await handles that for us, by waiting for the result of the awaited Promise to (“pseudo-synchronously”) move onto the next line within the same async function.

The result of executing this function is just as we saw before, where the attempt to overwrite the author property fails:

Using Async/Await with Airbrake

Finally, let’s pull it all together and see how to combine the advanced async/await methodology with the simplicity of handling errors and exceptions via Airbrake. This is accomplished within the testAsyncAwaitWithAirbrake(airbrake) method:

Again, we’ve managed to cut down the lines/characters by half from the previous Promise-based example that integrated with Airbrake. Best of all, since the Airbrake client’s notify(...) method returns a Promise, we can simply prefix that call with the await operator to halt execution at that point while waiting for the result. While we’ve then chosen to immediately invoke the then(...) method of the returned Promise object in this case, we could continue integrating async/await all the way down the chain, to completely get rid of Promise.then(...) method calls. For this case, however, then(...) doesn’t do anything that requires delayed processing/IO, so we can execute it immediately, once await comes back.

The result of executing this code is just as we saw before:

And, just as before, a new error corresponding with this generated notification appears on the Airbrake dashboard, with the Error Message of "Cannot update Author from Stephen King to Await w/ Airbrake Author."

That’s just a small taste of the power of the Airbrake-JS module and the newest async/await methodology introduced in the upcoming JavaScript release!