Technology · JavaScript

JavaScript Try Catch

Handle errors safely with try-catch blocks, finally, error types, and custom error handling patterns.

TL;DR
  1. 01Wrap risky code in try block to catch errors.
  2. 02Use catch to handle errors without crashing the app.
  3. 03Use finally to run cleanup code no matter what.

Basic Try-Catch

  • Wrap code that might throw errors inside a try block.
    try {
      const result = riskyFunction();
      console.log(result);
    } catch (error) {
      console.error("Something went wrong:", error);
    }
  • The catch block runs if any error is thrown inside the try.
  • The error variable contains the error object with details.
  • Code after the error inside try is not executed.
    try {
      throw new Error("Oops");
      console.log("This never runs");
    } catch (e) {
      console.log(e.message); // "Oops"
    }
  • Try-catch prevents the entire program from crashing.

Error Types

  • JavaScript has several built-in error types for different situations.
    try {
      JSON.parse("invalid json");
    } catch (error) {
      if (error instanceof SyntaxError) {
        console.log("JSON parsing failed");
      }
    }
  • Common errors: Error, SyntaxError, TypeError, ReferenceError, RangeError.
  • TypeError occurs when a value is the wrong type.
    try {
      const x = null;
      x.method(); // TypeError: Cannot read property 'method' of null
    } catch (e) {
      console.log(e instanceof TypeError); // true
    }
  • ReferenceError happens when a variable doesn't exist.
  • Check error types to handle different situations differently.

Finally Block

  • Use finally to run code that always executes, error or not.
    try {
      const file = openFile("data.txt");
      processFile(file);
    } catch (error) {
      console.error("Processing failed");
    } finally {
      closeFile(file);
    }
  • Finally runs even if there's no error in the try block.
  • Finally runs even if you return early from the catch.
    function getData() {
      try {
        return fetchData();
      } finally {
        cleanup();
      }
    }
  • Use finally for cleanup like closing files, releasing resources.
  • Reset UI state such as loading spinners inside a finally block.
    setLoading(true);
    try {
      await fetchData();
    } finally {
      setLoading(false); // always turns off the spinner
    }
  • Combine try, catch, and finally together for full error coverage.
    let connection;
    try {
      connection = openDB();
      return connection.query('SELECT * FROM users');
    } catch (error) {
      console.error('Query failed:', error.message);
    } finally {
      connection?.close();
    }

Custom Error Handling

  • Throw custom error messages for better clarity.
    try {
      if (!user) {
        throw new Error("User not found");
      }
    } catch (error) {
      console.error(error.message);
    }
  • Create custom error classes for specific situations.
    class ValidationError extends Error {
      constructor(message) {
        super(message);
        this.name = "ValidationError";
      }
    }
    
    try {
      if (age < 0) throw new ValidationError("Age must be positive");
    } catch (e) {
      if (e instanceof ValidationError) {
        console.log("Validation failed:", e.message);
      }
    }
  • Include relevant context in error messages for debugging.
  • Use stack traces to understand where errors originated.

Error Handling Patterns

  • Catch specific errors and provide fallback behavior.
    try {
      const config = JSON.parse(configString);
    } catch (error) {
      const config = getDefaultConfig();
    }
  • Re-throw errors after logging for upstream handling.
    try {
      operation();
    } catch (e) {
      logger.error(e);
      throw e;
    }
  • Use multiple catch blocks for different error types.
    try {
      riskyCode();
    } catch (error) {
      if (error instanceof TypeError) {
        // handle type error
      } else if (error instanceof SyntaxError) {
        // handle syntax error
      } else {
        // handle unknown error
      }
    }
  • Log errors with context to help with debugging later.

Tip: Always include a finally block when managing resources like file handles or database connections to ensure cleanup happens.

Warning: Catching all errors with a bare catch can hide bugs, so check error types and only handle expected errors gracefully.

JavaScript Template LiteralsJavaScript Destructuring