Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript Exceptions

Chapter 42 18 mins

Learning outcomes:

  1. The try...catch statements
  2. The finally statement
  3. The throw keyword

The try...catch statements

As stated in the previous JavaScript Exceptions — Introduction chapter, ECMAScript 3 introduced the try and catch statements to JavaScript, which at that time were, and even today are, a near-standard way to handle errors in code.

Let's see how exactly do they work...

The try statement tests a given piece of code for any errors.

It literally allows us to try executing a piece of code if it executes to completion.

If all goes well, great! However, if any error occurs amid the execution of the code, the corresponding catch block comes into the game.

The catch statement contains an error-handling piece of code.

It takes over when there is an error in the preceding try block.

The try...catch statements are instances of control-flow statements in JavaScript, some of which we've already explored in the JavaScript Conditions and JavaScript Loops units.

As with all control-flow statements, there is a precise syntax to using try and catch as shown below:

try {
   // Code to test for errors.
}
catch (errorObject) {
   // Code to handle any errors thrown in the try block above.
}

We start off with the try keyword, followed a block statement that contains the code to test.

Next comes the catch keyword (without any semicolon between the block of try and catch, similar to else), followed by a pair of parentheses (()) defining an identifier, followed by a block statement that contains the error-handling code.

When an error happens in a try block, an error object is generated based on one of the error classes discussed above, and then passed on to the corresponding catch block.

The catch block must be able to receive this object in some identifier, and that's where the pair of parentheses, after the catch keyword, comes into the equation. The pair of parentheses (()) defines a variable that'll hold the error object dispatched by the try statement, if any.

Simple?

Let's consider a quick example.

In the following code, we have the same set of statements as we had above accessing null like a function. However, this time the statements are encapsulated inside a try block:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('The error was handled.');
}

The consequence of this encapsulation is that when the statement value() is executed (in line 3), a TypeError is thrown as before, but instead of terminating the execution of the program, it's forwarded to the corresponding catch statement, keeping the normal execution flow of the program intact.

The console output produced by this code is not an error message, but just the text 'The error was handled.' as shown below:

The error was handled.

We could have just about any piece of code in the catch block.

In the code below, we check the type of error thrown with the help of the instanceof operator and then make a log based on the result:

try {
   var value = null;
   value();
}
catch (err) {
   if (err instanceof TypeError) {
      console.log('A value was used incorrectly.');
   }
   else {
      console.log('At least not a type error.');
   }
}
A value was used incorrectly.

The console output clearly tells us that there was an error of the type TypeError in the try block.

Let's now work with the err variable in the catch block. Consider the following code, where we log the name and the message of the error object thrown:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('Name:', err.name);
   console.log('Message:', err.message);
}

Here's the console output produced:

Name: TypeError Message: value is not a function

The message is automatically formed by the browser the moment the error is encountered, and it's the exact same one that's displayed when the error is logged in the console unhandled (in red color).

The finally statement

A third statement was added along with the try...catch statements in ECMAScript 3 to execute a piece of code regardless of any outcome in the try and catch blocks.

That statement is finally.

The finally statement is used to execute a piece of code at the end of a try...catch statement.

Syntactically, finally must come after a try statement but before the corresponding catch statement, if there is any.

Akin to try, finally doesn't have any pair of parentheses (()) as does the catch statement.

Here's its syntax:

try {
   // Code to test for errors.
}
catch (errorObject) {
   // Code to handle any errors thrown in the try block above.
}
finally {
   // Code to execute in the end.
}

Let's consider an example of the usage of finally.

In the snippet below, we have the same code as before, with the addition of a finally block:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('The error was handled.');
}
finally {
   console.log('Finally')
}

Let's see the console output produced:

The error was handled. Finally

As the try block run into an error, execution moves into the catch block, as a result, logging the text 'The error was handled.' Then once the catch block completes, execution moves into the finally block logging 'Finally'.

After the finally block, execution moves out of the entire try family of statements to the very next statement. Since in the case above, there was nothing beyond finally, its completion marked the completion of the code as well.

Now a fair question at this point would be that: if execution jumps out of try...catch onto the next statement anyway, what's the real point of finally?

For example, consider the following code:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('First');
}

console.log('Second');
First Second

If we were to incorporate this same logic but with finally in place, the console output would nonetheless still be the same:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('First');
}
finally {
   console.log('Second');
}
First Second

This then leads to the same question as before: what's the purpose of finally?

Well, technically, there is absolutely no benefit of finally in the code shown above. But there might be some case where we might find finally particularly helpful.

The thing is that finally is quite a powerful construct. It's guaranteed to execute at the end of a set of try and catch statements. This guarantee isn't there for code after the finally block.

As a matter of fact, finally gets executed even if a value is returned from the preceding try or catch blocks; it's executed even if the catch block runs into an error itself.

Let's consider an example of this.

Take a look at the code below without the finally block:

try {
   var value = null;
   value();
}
catch (err) {
   someRandomVariable;
}

console.log('Finally');

The catch block contains an error itself. Our job is to see the output produced by this code:

Uncaught ReferenceError: someRandomVariable is not defined at <anonymous>:6:4

Not surprisingly, the code throws an error and, most imporantly, the console.log() statement doesn't get executed either.

This example also illustrates the fact that it's only the try statement that's meant to test for errors; the catch block isn't concerned with testing a piece of code for errors. If there is any error in a catch block, it would get thrown.

Now, take a look over the code below, this time with finally:

try {
   var value = null;
   value();
}
catch (err) {
   someRandomVariable;
}
finally {
   console.log('Finally');
}
Finally
Uncaught ReferenceError: someRandomVariable is not defined at <anonymous>:6:4

The code still throws an error, but notice the first log. It confirms that the finally block got executed.

If you're very curious, you might want to know exactly how does the engine execute the code above. At first glance, it might seem that finally is executed before catch, and hence the log, but this isn't the case.

Is finally executed before catch?

To start off by answering this question, no, finally doesn't execute before catch.

As per the order of the statements, as defined by the grammar of JavaScript, finally always executes after the preceding catch block.

But this doesn't quite resonate well with the output in the code above. The string 'Finally' was logged before the error message, which hints us that the finally block was executed before catch.

And here comes the interesting part.

Internally, when the engine is executing the catch block, it checks for any errors. If an error is encountered, as always, it immediately halts execution in the catch block, but instead of turning to make an alert in the console for the error, it turns to the finally block, if there is any.

At the same time, the engine remembers that there is a pending error to deal with once the finally block is done executing.

Now two things could happen in the finally block:

  • It contains an error. In this case, this very error (in finally) is thrown and logged in the console. The error in the catch block is ignored.
  • It doesn't contain any error. In this case, the error from the catch block is thrown.

We can confirm all of this discussion with the help of a very simple piece of code, in fact, just with a couple of additions to our previous code.

In the following code, we have sprinkled a bunch of console.log() statements here and there to learn more about the order of execution of the try, catch and finally statements:

try {
   console.log('Try 1');
   var value = null;
   value();
   console.log('Try 2');
}
catch (err) {
   console.log('Catch 1');
   someRandomVariable;
   console.log('Catch 2');
}
finally {
   console.log('Finally');
}
console.log('Outside');

Let's now see the output produced here. But before we do that, try to reason yourself about the output. It really isn't that difficult.

Anyways, time to see the console:

Try 1 Catch 1 Finally
Uncaught ReferenceError: someRandomVariable is not defined at <anonymous>:9:4

Reasoning:

Execution begins with the try block. The string 'Try 1' is logged to the console. Thereafter, an error is encountered in line 4 and thus execution is halted beyond that point in the try block and instead shifted to the catch block.

In the catch block, the string 'Catch 1' is logged to the console. This confirms that catch does indeed execute after try. Thereafter, an error is encountered in line 9 and thus execution is halted beyond that point in the catch block and instead shifted to the finally block. The engine remembers that there is a pending error from the catch block to deal with.

In the finally block, the string 'Finally' is logged. This completes the finally block and likewise the engine resumes with the pending error from the catch block. This error is logged in the console.

And thereby, execution is halted beyond the finally block by virtue of the error thrown in the catch block.

Easy?

The throw keyword

As we saw above, there are many instances where the JavaScript engine automatically throws exceptions without having us to get involved in the error-generating process. It forms its own message and decides which error class to use, e.g. SyntaxError, TypeError, etc, all by itself.

Now most of the times, we are good with this setting. But sometimes, we might want to control the error dispatch. Said in other words, sometimes we might want to throw a custom error in a given scenario.

As a very simple example, suppose we create a function sum() to be used as a utility function in another JavaScript program:

function sum(a, b) {
   return a + b;
}

Since this will be used in another program (most probably not written by us), we can't guarantee that it'll be used in the correct way, which is to provide it with two arguments, both numbers.

In the console snippet below, we call this function with true and 10 as the arguments, and get 11 as the return value where we should've ideally got an error thrown because of the non-numeric value true:

sum(true, 10)
11

A better option is to check whether each of the arguments to sum() is a number and then throw an error if one is not. And in this regard, we can use the throw keyword as shown below:

function sum(a, b) {
   if (typeof a !== 'number' || typeof b !== 'number') {
      throw new TypeError('Arguments to sum() must only be numbers.');
   }
   return a + b;
}

Let's understand what exactly throw is.

The throw keyword was amongst the error-handling utilities introduced into JavaScript with ECMAScript 3.

throw is used to manually throw an error.

The syntax of throw is as follows:

throw errorDescription;

The throw keyword is followed by a value to describe the error thrown. Technically, errorDescription can be any valid value in JavaScript, but there is no point in using just about any arbitrary value.

The only sensible types of values suitable as errorDescription are numbers (for error codes), strings (for plain error messages) and, best of all, instances of the error classes that we discussed above, i.e. SyntaxError, TypeError, ReferenceError, etc.

If there is try block surrounding the throw statement, errorDescription is provided to the corresponding catch block.

Coming back to our example, recall that being provided with the wrong type of arguments is a TypeError concern. Likewise, we set up an if conditional to check the types of both a and b and throw an error if at least one of them is not a number.

Let's review the code:

function sum(a, b) {
   if (typeof a !== 'number' || typeof b !== 'number') {
      throw new TypeError('Arguments to sum() must only be numbers.');
   }
   return a + b;
}

And now let's try making the same erroneous call as before, i.e. sum(true, 10):

sum(true, 10)
Uncaught TypeError: Arguments to sum() must only be numbers. at sum (<anonymous>:3:13) at <anonymous>:1:1

Perfect! The code doesn't run and thus we succeed in preventing it from running in a scenario where the wrong type of arguments are provided.

Things to note

In this section, we learn about a couple of things to keep in mind while working with try, catch, finally and throw.

try doesn't entertain syntax errors

As we saw above, the try statement can be used to check if a given piece of code has any errors in it, and thereby handle those errors.

For instance, in the familiar code below, the statements inside the try block raise a TypeError exception and thus get the catch block to intervene:

try {
   var value = null;
   value();
}
catch (err) {
   console.log('The error was handled.');
}
The error was handled.

However, it's paramount to know that this isn't the case with syntax errors in code.

The try block can entertain all kinds of errors but not those in the grammar (syntax) of a code.

try {
   var = 10;
}
catch (err) {
   console.log('The error was handled.');
}
Uncaught SyntaxError: Unexpected token '='

This behavior of JavaScript shouldn't be considered a bad design decision. In fact, it's a really good one. A syntax error can be detected right when parsing code, and if one is found, the code shouldn't ideally be executed until that syntactic invalidity is resolved.

Thus, said in another way, the try block can only catch those errors that happen during the execution of a given program. Syntax errors almost always happen while parsing the program, and hence aren't handled by try.

catch and finally are optional

The catch and finally blocks aren't mandatory to be included for every try block.

This might be sensible for finally since we've already seen a couple of code examples without it, but we haven't seen any example without catch.

Consider the following code:

console.log('Before');
try {
   someRandomVariable;
}
console.log('After');

There is no catch block associated with the try statement, yet the error (precisely, the ReferenceError) inside try doesn't get delegated to the console as an alert message. The code executes normally making the following logs:

Before After

Omitting catch for a try statement is done only when we aren't interested in handling any errors in the try block.

The aim of try here is just to make sure that due to any possible errors in the code inside it, the entire script's execution doesn't come to a halt. If any error is encountered in try, it would be silently ignored due to no corresponding catch block, and execution would continue in its normal flow.

finally can come immediately after try

Thus far, we've seen the catch statement coming after try, and then the finally statement coming after catch.

However, this doesn't necessarily tell us that finally can also come immediately after try, given that there is no catch statement.

For instance, consider the following code:

try {
   someRandomVariable;
}
finally {
   console.log('Finally');
}

We have a try block, with an error, and a finally block to execute a piece of code after try. As before, there is no catch block, and so the error raised in try gets ignored silently, moving execution into finally.

Finally

Keep in mind that if there is a catch statement associated with a try statement, then it would be invalid to write the finally statement before it.

An illustration follows:

try {
   someRandomVariable;
}
finally {
   console.log('Finally');
}
catch (err) {
   console.log('The error was handled.')
}
Uncaught SyntaxError: Unexpected token 'catch'

While reading this code, the parser doesn't find any syntax error up until the end of the finally block — it's perfectly alright to include finally after try.

However, going beyond finally, it encounters catch where it doesn't expect it, and thus raises a SyntaxError clearly explaining the fact that an expected keyword catch was encountered.