JS: Throw Try Catch Finally

By Xah Lee. Date: . Last updated: .

Throw, try, catch, finally

keywords throw, try, catch, finally, are for dealing with errors. often known as “exception handling”.

Here is a typical usage syntax:

try {
 // do something here that that might throw. or throw yourself here
 throw "XYZ";
} catch (abc) {
 // abc is a parameter. its value is from throw
 console.log(abc); // prints XYZ
} finally {
 // the finally if exist, always runs
 console.log("Finally block run.");
}

// XYZ
// Finally block run.

How to use “throw”, “try/catch/finally”

The syntax of “throw” is:

throw expr;

The syntax of “try/catch/finally” is any of the following:

try {body} catch(e) {body}

try {body} catch {body}

try {body} catch(e) {body} finally {body}

try {body} finally {body}

When the “throw” statement is run, it redirect code execution to a outer “catch” block of a “try catch finally” statement. Then, run the “finally” block if there is one.

If no “try catch finally” is found, JavaScript stops running and prints the expression passed by “throw”.

The purpose of “try” block is just to contain “throw” (or contain function calls that may “throw”.).

When the “try catch finally” is run, code in the “try” block is executed. If throw happens in “try” block, the “catch” block is run, else skipped. The “finally” block (if any) is always run.

try {
 console.log(3);
} catch (e) {
 console.log(4);
} finally {
 console.log(5);
}

// result:
// 3
// 5

The “throw” and “try/catch/finally” block can be used without each other. But, typically, “throw” is used inside a “try” block. And you should do so in your code.

The throw statement

throw is like goto. It moves the execution to another place in code. The syntax is this:

throw expression;

// example of throw in a function, caught outside of the function

function ff() {
 throw 4;
}

try {
 ff();
} catch (ee) {
 console.log(ee);
}

When “throw” is called, execution jumps to the nearest outer block of catch in a “try/catch/finally” block. If the nearest outer block is a function block, then JavaScript tries to find the outer “catch” at where the function is called.

It repeats the process of jumping to the nearest outer block to find a “try/catch/finally”.

If no “try/catch/finally” is found, it's a error, usually JavaScript stops running.

If a “try/catch/finally” is found, the program flow continues at the “catch” block, and the argument of “throw” is passed to the parameter of “catch”.

// throw can be run by itself
throw 3;

// this is un-catched throw.
// js exits and prints 3
// throw in a try/catch block

try {
 throw 3;
} catch (e) {
 console.log(e);
}

// exits normally.
// result is printing 3
// throw in a try/catch block

try {
 if (false) {
  throw 3;
 }
} catch (e) {
 console.log(e);
}

// throw is never executed

The try statement

The try statement encloses a block of code in which an exceptional condition can occur, such as a runtime error or a throw statement. The catch clause provides the exception-handling code. When a catch clause catches an exception, its CatchParameter is bound to that exception.

ECMAScript 5.1 §12#sec-12.14

Here is a example of “try”, trying to call a function that may not exist.

try {
 yyy();
} catch (e) {
 console.log("aa");
}

console.log("bb");

// result is printing aa then bb

// without the try block, calling a non-existent function will exit abnormally, and bb won't be printed

Finally override

A return/throw in finally overrides a pending return or exception from try/catch.

function doIt() {
  try {
    throw "try";
  } catch {
    throw "catch";
  } finally {
    return "finally"; // swallows the throw
  }
}
doIt(); // "finally"

Error object

The throw is often used with a error object. That is, in throw expr;, the expr is a error object.

// throw an error object

try {
 throw new Error("xyz is wrong.");
} catch (ee) {
 console.log(ee);
}

However, Error object in JavaScript is independent of the “throw/try/catch” statement.

Write a tutorial on js throw try catch finally

write a tutorial on js throw try catch finally

ai answer

JavaScript Tutorial: throw, try, catch, and finally

JavaScript uses exceptions for error handling. When something goes wrong, you can throw an error. You wrap risky code in a try block so that if an error occurs, execution jumps to a catch block instead of crashing the program. A finally block always runs afterward — whether an error happened or not — which makes it ideal for cleanup.

This tutorial covers the syntax, how control flow works, practical examples, and best practices.

1. the throw statement

throw immediately stops the current function and passes control to the nearest catch (or terminates the program if nothing catches it).

throw expression;

You can throw any value (string, number, object), but you should almost always throw an Error (or a subclass) so you get a stack trace and a consistent .message / .name.

function getRectArea(width, height) {
  if (isNaN(width) || isNaN(height)) {
    throw new Error("Parameter is not a number!");
  }
  return width * height;
}

try {
  getRectArea(3, "A");
} catch (e) {
  console.error(e); // Error: Parameter is not a number!
}

Built-in error types you will see often:

2. try...catch

try {
  // code that might throw
} catch (error) {
  // handle the error
}

You can omit the error variable if you don’t need it (ES2019+):

function isValidJSON(text) {
  try {
    JSON.parse(text);
    return true;
  } catch {
    return false;
  }
}

You can also destructure the error:

try {
  throw new TypeError("oops");
} catch ({ name, message }) {
  console.log(name);    // "TypeError"
  console.log(message); // "oops"
}

Classic example

function getMonthName(mo) {
  const months = [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  ];
  const name = months[mo - 1];
  if (!name) {
    throw new Error("Invalid month code");
  }
  return name;
}

let monthName;
try {
  monthName = getMonthName(13);
} catch (e) {
  monthName = "unknown";
  console.error(e.message);
}

3. the finally block

finally always runs before control leaves the try/catch/finally construct — after a successful try, after a catch, or even if you return/throw/break/continue inside try or catch.

try {
  // risky work
} catch (e) {
  // handle
} finally {
  // cleanup — this always runs
}

You can also use try...finally without a catch.

Resource cleanup (the most common use)

openMyFile();
try {
  writeMyFile(theData); // might throw
} catch (e) {
  handleError(e);
} finally {
  closeMyFile(); // always close, even on error
}

Important nuance: return inside finally

If finally contains a return (or throw), it overrides any return/throw that happened in try or catch. Avoid control-flow statements in finally unless you really intend this behavior.

function test() {
  try {
    return "from try";
  } finally {
    return "from finally"; // this wins
  }
}
console.log(test()); // "from finally"

4. putting it together: try...catch...finally

All three blocks together:

try {
  console.log("Opening connection");
  throw new Error("Connection failed");
} catch (err) {
  console.error("Error:", err.message);
} finally {
  console.log("Closing connection");
}

// Output:
// Opening connection
// Error: Connection failed
// Closing connection

5. custom errors

Create your own error classes by extending Error. This lets callers distinguish error types with instanceof.

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

function checkAge(age) {
  if (age < 18) {
    throw new ValidationError("User must be 18 or older");
  }
  return "Access granted";
}

try {
  checkAge(16);
} catch (e) {
  if (e instanceof ValidationError) {
    console.log("Validation problem:", e.message);
  } else {
    throw e; // rethrow unexpected errors
  }
}

Rethrowing is a best practice: catch only what you know how to handle; let everything else bubble up.

6. nested try / catch

You can nest them when different layers need different handling:

try {
  try {
    riskyInner();
  } catch (inner) {
    console.log("Inner handled:", inner.message);
    throw inner; // or wrap and rethrow
  }
} catch (outer) {
  console.log("Outer handled:", outer.message);
}

7. asynchronous code

try...catch only catches synchronous throws and awaited rejections.

async function loadUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) {
      throw new Error(`Failed to fetch user ${id}`);
    }
    return await res.json();
  } catch (err) {
    console.error(err);
    // handle or rethrow
  } finally {
    // hide spinner, release lock, etc.
  }
}

For raw Promises (no await), use .catch() / .finally() on the promise chain instead.

Uncaught promise rejections and uncaught exceptions still go to the global handlers (window.onerror, unhandledrejection).

8. best practices

9. common pitfalls

Pitfall What happens
Forgetting await inside try The promise rejection is not caught by that try...catch.
return / throw in finally Overrides the original return/throw.
Catching everything and ignoring it Bugs become invisible.
Throwing non-Error values Harder to debug (no stack, inconsistent shape).
Using try...catch around every line Makes code noisy and can hide real problems.

Quick reference

try {
  // might throw
  if (badInput) throw new TypeError("bad input");
} catch (error) {
  if (error instanceof TypeError) {
    // handle this type
  } else {
    throw error; // rethrow
  }
} finally {
  // always runs (cleanup)
}

That’s the core of JavaScript exception handling. Start with throw + try...catch for validation and expected failures, add finally whenever you acquire a resource that must be released, and keep catch blocks focused.

JavaScript. Try, Catch, Throw, Error

Error handeling, exceptions