JavaScript Error Handling

JavaScript Error Handling

JavaScript programs sometimes encounter unexpected situations—such as undefined functions or invalid input—so the language provides mechanisms to detect, handle, and even throw custom errors. Error handling lets you gracefully manage issues at runtime, preventing crashes and enabling your code to respond or recover.

In JavaScript, you can use try and catch to safely run code that might fail, finally to execute cleanup actions regardless of success, and throw to create custom errors yourself. Beyond that, nested structures and async functions support structured error control. This article walks you through each feature with spirited examples that bring the ideas to life.

Using try and catch

You begin error handling with a try block where you place code that might fail. If an error occurs inside the try, control immediately jumps to the catch block, giving you access to the error and a chance to handle it.

Imagine a magical function castSpell() that may not exist yet. Here’s how you might handle its invocation:

<!DOCTYPE html>
<html>
<head>
  <title>Try Catch Example</title>
</head>
<body>

<script>

  try {
    castSpell('disappearify');
  } catch (error) {
    console.log('That spell failed:', error.message);
  }

</script>

</body>
</html>

In this example, calling castSpell('disappearify') causes an error—but our code catches it. The catch prints a playful message along with the error’s message, preventing your script from crashing.

Using the finally Block

After running code in try or handling an error in catch, finally ensures that important cleanup always happens—regardless of whether an error occurred.

Picture opening a mystical chest with a trap:

<!DOCTYPE html>
<html>
<head>
  <title>Finally Example</title>
</head>
<body>

<script>

  function closeChest() {
    console.log('Closing chest...');
  }

  try {

    openChest();
    grabTreasure();

  } catch (error) {
    console.log('Oops, trap triggered!');
  } finally {
    closeChest();
  }

</script>

</body>
</html>

Here, closeChest() executes no matter what happened in try. It ensures that the chest is always closed, even if grabbing the treasure fails or triggers an exception.

Throwing Custom Errors with throw

JavaScript lets you create your own errors using throw new Error(). This is ideal when running custom logic that fails due to application-specific conditions.

Consider a magical world where equipping a “Cursed Sword” is forbidden:

<!DOCTYPE html>
<html>
<head>
  <title>Throw Example</title>
</head>
<body>

<script>

  function equipItem(item) {

    if (item === 'Cursed Sword') {
      throw new Error('This item is cursed!');
    }

    console.log('You have equipped the', item);

  }

  try {
    equipItem('Cursed Sword');
  } catch (error) {
    console.log('Cannot equip:', error.message);
  }

</script>

</body>
</html>

If you try to equip the forbidden item, throw creates a new error and catch handles the message, giving the user feedback about why the action failed.

Nested Try‑Catch Blocks

Sometimes your code has multiple layers of operations, each of which may fail. Nested try blocks let you manage errors specifically at different levels.

Here’s a dungeon explorer scenario:

<!DOCTYPE html>
<html>
<head>
  <title>Nested Try Example</title>
</head>
<body>

<script>

  try {

    enterRoom('north');

    try {
      openDoor('wooden');
    } catch (doorError) {
      console.log('Wooden door jammed:', doorError.message);
    }

    enterRoom('east');

  } catch (roomError) {
    console.log('Failed to enter room:', roomError.message);
  }

</script>

</body>
</html>

The inner catch handles door-related problems, while the outer block catches broader room-access issues. This approach helps separate and handle errors at the right level.

Accessing Error Properties

When an error is caught, the catch parameter holds an Error object. You can inspect its properties like name, message, and stack for more details.

Below is an example of trying to read from a scroll that might be missing, and logging detailed error info:

<!DOCTYPE html>
<html>
<head>
  <title>Error Object Example</title>
</head>
<body>

<script>

  try {
    readScroll(null);
  } catch (e) {
    console.log('Name:', e.name);
    console.log('Message:', e.message);
    console.log('Stack trace:', e.stack);
  }

</script>

</body>
</html>

This code prints the error’s name (e.g. "ReferenceError"), the message, and the stack trace, giving insight into what went wrong and where.

Handling Errors in Functions

Functions often need internal error management and return values upon failure. Throwing and catching inside functions makes them safer to call.

Imagine brewing a potion with the wrong ingredient:

<!DOCTYPE html>
<html>
<head>
  <title>Function Error Example</title>
</head>
<body>

<script>

  function brewPotion(ingredient) {

    if (ingredient !== 'unicorn hair') {
      throw new Error('Potion failed. Wrong ingredient!');
    }

    return 'Potion brewed!';

  }

  try {

    const result = brewPotion('goblin toe');
    console.log(result);

  } catch (error) {
    console.log('Error while brewing:', error.message);
  }

</script>

</body>
</html>

The function throws if the wrong ingredient is given. The try around it catches the error and logs it, keeping the code flow intact.

Handling Errors in Asynchronous Code

You can also use try...catch inside async functions to handle errors that occur during await operations.

Here’s a mystical summoning example:

<!DOCTYPE html>
<html>
<head>
  <title>Async Error Example</title>
</head>
<body>

<script>

  async function summonFamiliar(name) {

    if (name !== 'Raven') throw new Error('Only Ravens respond to the call!');
    return 'Raven has arrived!';

  }

  (async () => {

    try {

      const result = await summonFamiliar('Owl');
      console.log(result);

    } catch (e) {
      console.log('Summoning failed:', e.message);
    }

  })();

</script>

</body>
</html>

Using async/await, we can await a function that may throw an error and catch it gracefully, just like with synchronous code.

Conclusion

You’ve now seen how to handle runtime errors in JavaScript using try, catch, finally, and throw. You experienced nested error blocks, custom error throwing, asynchronous error handling, and inspecting error details. With these tools, you can control the flow of your code and respond creatively whenever something unexpected happens.

References

If you’re curious to explore further or want to double-check what you’ve learned, these trusted documentation pages offer more detailed explanations and examples:

Scroll to Top