facebook twitter
Webucator's Free JavaScript Tutorial

Lesson: Errors and Exceptions

Welcome to our free JavaScript tutorial. This tutorial is based on Webucator's Introduction to JavaScript Training course.

JavaScript provides several methods for catching and handing errors, the most useful of which is trycatch/finally.

Lesson Goals

  • Learn to use JavaScript's try/catch/finally to handle errors

Runtime Errors

A runtime error is an error that occurs while a program is being executed. A runtime error can be the result of invalid user input, a browser change, or bad data sent from the server.

It is the programmer's job to anticipate, "catch," and "handle" potential runtime errors..

Completely Unhandled Errors

Look at this seemingly trivial code sample:

Code Sample:

ErrorsExceptions/Demos/simple-bug.html
---- C O D E   O M I T T E D ----

function getInput() {
  var name = prompt('Type your name', '');
  alert('Your name has ' + name.length + ' letters.');
}
getInput();
---- C O D E   O M I T T E D ----

Code Explanation

It may not be obvious, but this code has a bug waiting to break free. If the user clicks Cancel or presses Esc the prompt() function will return null, which will cause the next line to fail with a null reference error.

If you as a programmer don't take any step to deal with this error, the user won't know what went wrong. The error message will most likely be hidden in the console: Error dialog

Globally Handled Errors

The window object has an event called error for which we can add an event handler, listening for global errors. The next demo shows an example of this:

Code Sample:

ErrorsExceptions/Demos/simple-bug-onerror.html
---- C O D E   O M I T T E D ----

window.addEventListener("error", function (e) {
  alert('Error: ' + e.error.message);
  return true;
});

function getInput() {
  var name = prompt('Type your name', '');
  alert('Your name has ' + name.length + ' letters.');
}
getInput();
---- C O D E   O M I T T E D ----

Code Explanation

If the user presses Esc when the prompt asks for a name, our event handler fires.

Here's the alert that we show to the user: Error message

This makes sure the user knows there was an error, but it doesn't help resolve the error. Also, it will let the user know about all errors that occur, even innocuous ones.

Structured Error Handling

The best way to deal with errors is to detect them as close as possible to where they occur. This will increase the chance that we know what to do with the error. To that effect, JavaScript implements structured error handling, via the try...catch...finally block, also present in many other languages:

Syntax

try {
	// statements;
} catch (error) {
	// statements;
} finally {
	// statements;
}

The idea is simple. If anything goes wrong in the statements that are inside the try block then the statements in the catch block will be executed. The finally block is optional and, if present, is always executed last, whether or not an error is caught.

The finally Block

In JavaScript, you're unlikely to need the finally block except for advanced code involving nested try / catch blocks.

Let's fix our example to catch that error:

Code Sample:

ErrorsExceptions/Demos/simple-bug-try-catch.html
---- C O D E   O M I T T E D ----

window.addEventListener("error", function (e) {
    alert('Error: ' + e.error.message);
  return true;
});

function getInput() {
  try {
    var name = window.prompt('Type your name', '');
    alert('Your name has ' + name.length + ' letters.');      
  } catch (error) {
    alert('The error was: ' + error.name + 
    '\n The error message was: ' + error.message);
  }
}
getInput();
---- C O D E   O M I T T E D ----

Code Explanation

The error object in the catch block has two important properties: name and message.

  • name - contains the type of error, which we could use to decide how we handle the error.
  • message - contains the error message.

With that in place, if we reload the page and cancel out of the prompt, we will get the following alert: Error message

It's a good programming practice to only handle the error on the spot if you are certain of what it is and if you actually have a way to take care of it (other than just suppressing it altogether.) To better target our error handling code, we will change it to only handle errors named "TypeError", which is the error name that we have identified for this bug.

Code Sample:

ErrorsExceptions/Demos/simple-bug-try-catch-specific.html
---- C O D E   O M I T T E D ----

window.addEventListener("error", function (e) {
    alert('Error: ' + e.error.message);
  return true;
});

function getInput() {
  try {
    var name = window.prompt('Type your name', '');
    alert('Your name has ' + name.length + ' letters.');      
  } catch (error) {
    if (error.name == 'TypeError') {
      alert('Please try again.');
      getInput();
    } else  {
      throw error;
    }
  }
}
getInput();
---- C O D E   O M I T T E D ----

Code Explanation

Now if a different type of error happens, that error will not be handled. The throw statement will forward the error as if we never had this try...catch...finally block. It is said that the error will bubble up.

Throwing custom errors

We can use the throw statement to throw our own errors. While there are several ways to do this, a simple way is to throw a new Error object:

throw new Error('The given color is not a valid color value.');

Try/Catch

Duration: 10 to 15 minutes.

In this exercise, you will handle potentially-problematic user input in a simple calculator, designed to return the quotient of two user-entered numbers.

  1. Open ErrorsException/Exercises/try-catch.html for editing.
  2. Within a try block:
    1. Get the values of dividendField and divisorField and convert them to floats.
    2. If the divisor is 0, throw a new Error with the message "Cannot divide by zero."
    3. If either the dividend or the divisor is not a number, throw a new Error with the message "Please enter numbers."
    4. Divide the dividend by the divisor and assign the result to a variable. If that result is not a number, throw a new Error with the message "Cannot solve this equation."
    5. Write the equation with the result out to the innerHTML of the msgField output. Note that this should only happen if none of the above checks resulted in errors.
  3. Within the catch block, write "There was a problem: " followed by the error message out to the innerHTML of the msgField output.
  4. Test your solution in a browser.

Solution:

ErrorsExceptions/Solutions/try-catch.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="../normalize.css">
<link rel="stylesheet" href="../styles.css">
<script>
function displayAnswer() {
  var dividendField = document.getElementById('dividend');
  var divisorField = document.getElementById('divisor');
  var msgField = document.getElementById('msg');

  try {
    var dividend = parseFloat(dividendField.value);
    var divisor = parseFloat(divisorField.value);
    if (divisor === 0) {
      throw new Error('Cannot divide by zero.');
    }
    if ( isNaN(dividend) || isNaN(divisor) ) {
      throw new Error('Please enter numbers.');
    }
    var quotient = dividend / divisor;
    if ( isNaN(quotient) ) {
      throw new Error('Cannot solve this equation.');
    }

    msgField.innerHTML = String(dividend) + " / " + 
      String(divisor) + " = " + String(quotient);
  }
  catch (e) {
    msgField.innerHTML = 'There was a problem: ' + e.message;
  }
}

window.addEventListener('load', function() {
  var equals = document.getElementById('equals');
  equals.addEventListener('click', displayAnswer, false)
});;
</script>
<title>Try/Catch</title>
</head>
<body id="exercise">
<main>
  <input type="text" id="dividend" class="operator"> /
  <input type="text" id="divisor" class="operator">
  <button id="equals">=</button>
  <output id="msg"></output>
</main>
</body>
</html>