Skip to content
Theory · Errors · lesson 1 of 10

throw and try…catch

What you'll learn

What you'll learn:

  • throw an error with throw and catch it in try…catch;
  • explain how an error travels up the chain of calls to the nearest catch;
  • clean up in finally whatever the outcome;
  • tell errors apart by name and instanceof, and create your own error classes.
Three desks in a chain; a note from the desk divide flies up along an arrow throw into a net catch on the top one

Picture a chain of tasks. A manager asks an assistant to prepare a report, and the assistant asks an intern to count the figures. The intern was given an empty table and cannot count anything.

The intern will not make up an answer. He throws a note back up with the reason: "the table is empty". The note flies back along the chain, and the first person who prepared for such a case catches it. If nobody prepared, all the work stops.

That is how errors work in JavaScript. The errors of earlier themes — ReferenceError, TypeError, RangeError — stopped the program. A function can stop it too when it gets a value it cannot work with. Division by zero is not an error in JavaScript and gives Infinity, so the function divide checks the divisor:

JavaScript
function divide(a, b) {
    if (b === 0) {
        throw new Error("division by zero");
    }
    return a / b;
}

console.log(divide(6, 3)); // 2
console.log(divide(1, 0)); // ✗
console.log("end");
TEXT
Error: division by zero

Let's see what happened:

  1. divide(6, 3) passed the check and returned 2, and the program printed it.
  2. divide(1, 0) saw a zero divisor and threw an error.
  3. Nobody caught this error, and the program stopped, as a ReferenceError did in the first theme.
  4. The line end was never printed.

Throwing an error

The expression new Error("division by zero") creates an error object — that very note. Error is a built-in class, and the string in parentheses becomes the error's text.

The throw statement throws this object. Here is what happens then:

  • the function stops at that line, as at a return, but returns no value;
  • the error goes out to the code that called the function and stops that code too;
  • so it travels on, call after call, until someone catches it.

The errors of earlier themes work the same way: JavaScript itself throws TypeError and ReferenceError. Nobody caught them, so the program stopped.

Catching an error

Code that may throw an error goes into a try block. If an error is thrown in it, the rest of the block is skipped and execution moves to the catch block. There the error is caught: the program does not stop and goes on after catch.

JavaScript
function divide(a, b) {
    if (b === 0) {
        throw new Error("division by zero");
    }
    return a / b;
}

try {
    console.log(divide(6, 3)); // 2
    console.log(divide(1, 0));
    console.log("skipped");
} catch (e) {
    console.log(e.message); // division by zero
}
console.log("end"); // end

Let's go step by step:

  1. divide(6, 3) returns 2, and the first line of the try block prints it.
  2. divide(1, 0) throws an error, and the line skipped does not run.
  3. Execution moves to catch, which prints the error's text.
  4. After catch the program goes on and prints end.

The parentheses after catch hold the name e: the thrown object lands in it. The message property of that object holds the text passed to new Error. If the try block finishes without errors, the catch block does not run at all.

throw works the same inside a function and directly in a try block: the lines of the block after it are skipped.

The error's path through calls

Recall the call stack from the recursion theme: each unfinished call takes its own record in it. A thrown error removes these records one by one, from the top down. In every function it passes through, the remaining lines are skipped. Only a try…catch around the call can stop the error.

Press "Next step" and watch the step where check throws: the frames of check and double vanish at once, and the line return v * 2 never runs.

LiveAn error's path through the call stackstep 1 of 10 · frames: 1

Inside try the program calls double(3) and waits for the result.

The error removes frames until it reaches a try

Here the note picture stops working. A person in the chain could decide whether to catch the note or not. A function without try has no choice: the error stops it on its own.

Interviewers like to ask what happens to an error thrown deep inside a nested call. It travels up the call stack to the nearest try…catch, and if there is none, the program stops.

Common mistake

A variable is declared inside try and read after it:

JavaScript
try {
    const result = divide(6, 3);
} catch (e) {
    console.log(e.message);
}
console.log(result); // ✗
TEXT
ReferenceError: result is not defined

A try block is a {} block like the one of an if. And const and let exist only in their own block. Declare the variable with let before try and only assign it inside: let result;, then result = divide(6, 3);.

Key points

  • throw throws an error object, such as new Error("text"), and stops the function.
  • An error travels up the call stack to the nearest catch, skipping every line after throw.
  • catch (e) receives the thrown object, and e.message holds its text.
  • An uncaught error stops the program.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way