Handling Exceptions in JavaScript

John Au-Yeung
Nov 20, 2019 · 9 min read
Image for post
Image for post
Photo by Max Chen on Unsplash

Like any programs, JavaScript will encounter error situations, for example, like when JSON fails to parse, or null value is encounter unexpectedly in a variable. This means that we have to handle those errors gracefully if we want our app to give users a good user experience. This means that we have to handle those errors gracefully. Errors often come in the form of exceptions, so we have to handle those gracefully. To handle them, we have to use the try...catch statement to handle these errors so they do not crash the program.

Try…Catch

To use the try...catch block, we have to use the following syntax:

For example, we can write the following code to catch exceptions:

In the code above, we were trying to get a property from undefined , which is obviously not allowed, so an exception is thrown. In the catch block, we catch the ‘TypeError: Cannot read property ‘prop’ of undefined’ that’s caused by running undefined.prop and log the output of the exception. So we get the error message outputted instead of crashing the program.

The try...catch statement has a try block. The try block must have at least one statement inside and curly braces must always be used, event for single statements. Then either the catch clause or finally clause can be included. This means that we can have:

The catch clause has the code that specifies what to do when an exception is thrown in the try block. If they try block didn’t succeed and an exception is thrown, then the code in the catch block will be ran. If all the code in the try block is ran without any exception thrown, then the code in the catch block is skipped.

The finally block executes after all the code the try block or the catch block finishes running. It always runs regardless if exceptions are thrown or not.

try blocks can be nested within each other. If the inner try block didn’t catch the exception and the outer one has a catch block, then the outer one will catch the exception thrown in the inner try block. For example, if we have:

If we run the code above, we should see ‘Inner finally block runs’ and ‘Outer catch block caught: TypeError: Cannot read property ‘prop’ of undefined’ logged, which is what we expect since the inner try block didn’t catch the exception with a catch block so the outer catch block did. As we can see the inner finally block ran before the outer catch block. try...catch...finally runs sequentially, so the code that’s added earlier will run before the ones that are added later.

The catch block that we wrote so far are all unconditional. That means that they catch any exceptions that were thrown. The error object holds the data about the exception thrown. It only holds the data inside the catch block. If we want to keep the data outside it then we have to assign it to a variable outside the catch block. After the catch block finishes running, the error object is no longer available.

The finally clause contains statements that are excepted after the code in the try block or the catch block executes, but before the statements executed below the try...catch...finally block. It’s executed regardless whether an exception was thrown. If an exception is thrown, then statements in the finally block is executed even if no catch block catches and handles the exception.

Therefore, the finally block is handy for making our program fail gracefully when an error occurs. For example, we can put cleanup code that runs no matter is an exception is thrown or not, like for close file reading handles. The remaining code in a try block doesn’t executed when an exception is thrown when running a line in the try block, so if we were excepted to close file handles in the try and an exception is thrown before the line that closes the file handle is ran, then to end the program gracefully, we should do that in the finally block instead to make sure that file handles always get cleaned up. We can just put code that runs regardless of whether an exception is thrown like cleanup code in the finally block so that we don’t have to duplicate them in the try and catch blocks. For example, we can write:

In the code above, the closeFile function always run regardless of whether an exception is thrown when the writeFile is run, eliminating duplicate code.

We can have nested try blocks, like in the following code:

If we look at the console log, we should see that ‘finally runs’ comes before ‘exception caught error.’ This is because everything in the try...catch block is ran line by line even if it’s nested. If we have more nesting like in the following code:

We see that we get the same console log output as before. This is because the first inner try block didn’t catch the exception, so the exception is propagated to and caught by the outer catch block. If we want to second try block to run, then we have to add a catch block to the first try block, like in the following example:

Now we see the following message logged in order: ‘first catch block runs’, ‘first finally runs’, ‘second finally runs’, ‘exception caught error2’. This is because the first try block has a catch block, so the the exception caused by the throw new Error('error') line is now caught in the catch block of the first inner try block. Now the second inner try block don’t have an associated catch block, so error2 will be caught by the outer catch block.

We can also rethrow errors that were caught in the catch block. For example, we can write the following code to do that:

As we can see, if we ran the code above, then we get the following logged in order: ‘error error’, ‘finally block is run’, and ‘outer catch block caught error’. This is because the inner catch block logged the exception thrown by throw new Error(‘error’) , but then after console.error(‘error’, error.message); is ran, we ran throw error; to throw the exception again. Then the inner finally block is run and then the rethrown exception is caught by the outer catch block which logged the error that was rethrown by the throw error statement in the inner catch block.

Since the code runs sequentially, we can run return statements at the end of a try block. For example, if we want to parse a JSON string into an object we we want to return an empty object if there’s an error parsing the string passed in, for example, when the string passed in isn’t a valid JSON string, then we can write the following code:

In the code above, we run JSON.parse to parse the string and if it’s not valid JSON, then an exception will be thrown. If an exception is thrown, then the catch clause will be invokes to return an empty object. If JSON.parse successfully runs then the parsed JSON object will be returned. So if we run:

Then we get an empty object on the first line and we get {a: 1} on the second line.

Image for post
Image for post
Photo by Sharon McCutcheon on Unsplash

Try Block in Asynchronous Code

With async and await , we can shorten promise code. Before async and await, we have to use the then function, we make to put callback functions as an argument of all of our then functions. This makes the code long is we have lots of promises. Instead, we can use the async and await syntax to replace the then and its associated callbacks as follows. Using the async and await syntax for chaining promises, we can also use try and catch blocks to catch rejected promises and handle rejected promises gracefully. For example , if we want to catch promise rejections with a catch block, we can do the following:

In the code above, since we rejected the promise that we defined in the try block, the catch block caught the promise rejection and logged the error. So we should see ‘error’ logged when we run the code above. Even though it looks a regular try...catch block, it’s not, since this is an async function. An async function only returns promises, so we can’t return anything other than promises in the try...catch block. The catch block in an async function is just a shorthand for the catch function which is chained to the then function. So the code above is actually the same as:

We see that we get the same console log output as the async function above when it’s run.

The finally block also works with the try...catch block in an async function. For example, we can write:

In the code above, since we rejected the promise that we defined in the try block, the catch block caught the promise rejection and logged the error. So we should see ‘error’ logged when we run the code above. The the finally block runs so that we get ‘finally is run’ logged. The finally block in an async function is the same as chaining the finally function to the end of a promise so the code above is equivalent to:

We see that we get the same console log output as the async function above when it’s run.

The rules for nested try...catch we mentioned above still applies to async function, so we can write something like:

This lets us easily nest promises and and handle their errors accordingly. This is cleaner than chaining the then , catch and finally functions that we did before we have async functions.

To handle errors in JavaScript programs, we can use the try...catch...finally blocks to catch errors. This can be done with synchronous or asynchronous code. We put the code that may throw exceptions in the try block, then put the code that handles the exceptions in the catch block. In the finally block we run any code that runs regardless of whether an exception is thrown. async functions can also use the try...catch block, but they only return promises like any other async function, but try...catch...finally blocks in normal functions can return anything.

New JavaScript + Web Development articles every day.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store