JavaScript Error Handling: Try, Catch, Finally, Throw

JavaScript provides a robust error-handling mechanism to detect, catch, and resolve runtime errors without crashing the entire application. By utilizing the try, catch, finally, and throw statements, developers can control application flow, generate custom error messages, and execute necessary cleanup tasks seamlessly.


The try Block

The try statement wraps the code that might potentially throw an exception. If an error occurs inside this block, execution immediately halts, and control is transferred directly to the corresponding catch block.

try {
  let result = riskyOperation();
  console.log(result);
} catch (error) {
  // Handled here
}

If no errors occur within the try block, the catch block is completely skipped.


The catch Block

The catch statement defines a block of code to execute if an exception is thrown in the try block. It receives the error object as an argument, providing details about what went wrong.

The standard error object contains two primary properties: - name: The type of error (e.g., ReferenceError, TypeError). - message: A human-readable description of the error.

try {
  console.log(nonExistentVariable);
} catch (error) {
  console.error("Error Name: " + error.name);
  console.error("Error Message: " + error.message);
}

In modern JavaScript (ES2019+), the error parameter is optional if you do not need to inspect the error object:

try {
  JSON.parse(invalidJsonString);
} catch {
  console.error("Failed to parse JSON string.");
}

The throw Statement

The throw statement allows you to create custom errors. When an exception is thrown, the current function stops executing, and control passes to the nearest enclosing catch block.

You can throw any data type (strings, numbers, booleans, objects), but it is best practice to throw standard JavaScript Error objects to preserve stack traces.

function validateAge(age) {
  if (age < 0) {
    throw new RangeError("Age cannot be a negative number.");
  }
  if (typeof age !== "number") {
    throw new TypeError("Age must be a numeric value.");
  }
  return true;
}

try {
  validateAge(-5);
} catch (error) {
  console.error(`${error.name}: ${error.message}`);
}

The finally Block

The finally statement executes after the try and catch blocks, regardless of whether an error was thrown or caught. It is typically used for cleanup routines, such as closing file handles, stopping loaders, or resetting connection states.

function processData() {
  let isProcessing = true;

  try {
    // Perform data operations
    performComputation();
  } catch (error) {
    console.error("Computation failed:", error.message);
  } finally {
    // Always runs regardless of success or failure
    isProcessing = false;
    console.log("Processing finished. State reset.");
  }
}

Even if a return statement is encountered inside try or catch, the finally block will execute before the function exits.


Complete Implementation Example

function parseUserData(jsonString) {
  let connectionOpen = true;

  try {
    let user = JSON.parse(jsonString);

    if (!user.email) {
      throw new Error("User record missing required 'email' field.");
    }

    console.log(`User ${user.name} processed successfully.`);
    return user;
  } catch (error) {
    console.error(`Processing Error: ${error.message}`);
  } finally {
    connectionOpen = false;
    console.log("Database connection closed.");
  }
}

// Example usage
parseUserData('{"name": "Alice"}');