TOC
Error handling:

Catching errors (try/catch)

In the introduction to this chapter, we discussed the difference between syntax errors and runtime errors. Syntax errors are usually easy to catch, because the interpreter (the browser, if you use JavaScript for web) will immediately spot them and prevent the code from running.

Runtime errors are harder to deal with, because they can only be spotted after the execution of the code has started, and only if the buggy part of the code is reached. They should still be taken very serious though, because if they are reached, they can prevent the rest of your code from being executed and essentially cause your website/application to stop working as intended.

Runtime errors that are not handled are often referred to as "uncaught errors" or "uncaught exceptions", likely referring to mechanism that will allow you to catch them: The try..catch block. Whenever you have a block of code where you do something which has the potential to fail, you should consider surrounding it with a try..catch block.

Let's try an example. The following code calls a function, but an exception will be thrown. In this case, it's thrown because I haven't declared the function yet, but this is just for the sake of the example - in the real world, it might as well fail because the function somehow fails and then throws an exception:

let result = AddNumbers(2, 40);
alert(result);
alert("Done!");

The resulting error will appear in your console and look like this:

Uncaught ReferenceError: AddNumbers is not defined

And you won't see any of the alerts, because this uncaught runtime error causes the execution of your code to be halted. Let's fix that, using a try..catch block.

try..catch blocks

It works by having two parts (possibly three, but more on that later): The try-part, where the code that might cause an error is placed, and the catch-part, where we end up IF an error occur in the try-part, allowing us to either do something about the problem and/or let the user know about it, or simply nothing at all.

It will look like this:

try
{
	// Do potentially dangerous things here
}
catch(error)
{
	// Handle error here, if needed
}

With that in mind, let's rewrite the above example to use a try..catch block:

try
{
	let result = AddNumbers(2, 40);
	alert(result);
}
catch(error)
{
	
}
alert("Done!");

You should notice a couple of things here. First of all, the code still fails - we're still trying to call a function which hasn't been declared, which will obviously fail. However, we don't get any errors in the console, because now we have caught the error, letting JavaScript know that it doesn't have to worry about this error.

Secondly, notice that we now get the "Done!" alert, which we didn't get before. Why? Because it's outside the try..catch block, and since we catch and contain the error inside of this block, the code after it is allowed to run, proving just perfectly why we needed to use a try..catch block here.

Thirdly, you will notice that I don't really do anything in the catch-part of the block. Sometimes you may need to do something in an effort to fix the unexpected result of the try-part. You may also want to log the error somewhere, and perhaps even let the user know about it. Or you may just want to completely ignore this error, know that it has been caught. It all depends on the specific situation.

Dealing with the error

In the above example, you may have wondered about the (error) right after the catch keyword: What is it, and why aren't we using it? Well, by default, the catch statement will catch the error for you and deliver it to the following block of code, sort of like a parameter. You get to name it - JavaScript will pass it in no matter what you call it.

So, in the the above example, we are catching any errors in the try-part and information about the error will be passed into the catch-part as a parameter called error. You are free to simply ignore this parameter, if you don't need information about the error, just like you are free to do nothing when an error is caught.

However, sometimes it can be useful to look at the information about the error to help remedy the problem, or simply to log it. The parameter will be of the Error type, or at least inherit from this type, meaning that you will always be provided with basic information about the error. Here's an example:

try
{
	let result = AddNumbers(2, 40);
	alert(result);
}
catch(error)
{
	let msg = "Error!\n\n";
	msg += "Type: " + error.name + "\n";
	msg += "Description: " + error.message + "\n";
	alert(msg);
}
alert("Done!");

In this example, I use information from the error parameter to describe the error. This information could easily be logged, or used to give a more user-friendly error message to the user, or whatever you can think of. The point is that the information is available and can be very useful.

Optional catch binding

On the other hand, sometimes you don't really care about the error that was thrown - you simply want to ignore it! You are of course still free to do that, as we saw previously, but you still have to declare the error parameter as part of the catch block. Or actually, not anymore. As part of the ES2019 specification, you may now omit the (error) part of the catch-block, if you don't need it:

try
{
	let result = AddNumbers(2, 40);
	alert(result);
}
catch { }
alert("Done!");

This is referred to as optional catch binding, and while its currently supported by all modern browsers, there are still a lot of older browsers out there which doesn't support. If you consider using this feature in your code, you may want to check this browser compatibility chart to see if it makes sense for your specific project.

Summary

Handling errors in JavaScript becomes a lot easier when using try..catch blocks, and they are not specific to JavaScript - you will find them, in slightly different forms, in lots of modern programming languages like C#, PHP, Java and so on.

Whenever you write code that could potentially result in an error, you need to consider if this block of code should be within a try..catch block. On the other hand, you don't want to make your code overly complex with try..catch blocks all over the place. As with any programming task, this is all about finding the proper level.

From this article, you have hopefully learned the most basic aspects of using a try..catch block and dealing with the error involved, but there's more to error handling, as we'll discover in the next article.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!