Asher Cohen
Back to posts

Synchronous Error Handling in JavaScript

Master try/catch/finally for robust error handling in synchronous JavaScript code

Introduction

Error handling is crucial for building robust applications. Synchronous error handling in JavaScript uses try/catch/finally blocks to gracefully handle exceptions.

Try/Catch/Finally Basics

The Try Block

Contains code that might throw:

function toUppercase(string) {
  if (typeof string !== 'string') {
    throw TypeError('Wrong type given, expected a string');
  }
  return string.toUpperCase();
}

try {
  toUppercase(4); // Throws error
} catch (error) {
  console.error(error.message);
}

The Catch Block

Handles exceptions:

try {
  riskyOperation();
} catch (error) {
  console.error('Operation failed:', error.message);
  logErrorToService(error);
  showUserMessage('Something went wrong');
}

The Finally Block

Always runs, perfect for cleanup:

try {
  const data = loadData();
  processData(data);
} catch (error) {
  console.error('Failed:', error.message);
} finally {
  hideLoadingSpinner(); // Always runs
}

Error Types

Built-in Errors

// TypeError
try {
  null.someMethod();
} catch (error) {
  if (error instanceof TypeError) {
    // Handle type error
  }
}

// SyntaxError
try {
  JSON.parse('invalid json');
} catch (error) {
  if (error instanceof SyntaxError) {
    // Handle syntax error
  }
}

Custom Errors

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

try {
  throw new ValidationError('Email required', 'email');
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(`Field ${error.field}: ${error.message}`);
  }
}

Best Practices

Be Specific

try {
  doSomething();
} catch (error) {
  if (error instanceof NetworkError) {
    retry();
  } else {
    throw error;
  }
}

Always Log Errors

try {
  riskyOperation();
} catch (error) {
  console.error('Operation failed:', {
    message: error.message,
    stack: error.stack
  });
}

Clean Up in Finally

let connection;
try {
  connection = connect();
  doWork(connection);
} finally {
  if (connection) {
    connection.close(); // Always close
  }
}

Important: Try/Catch is Synchronous Only

// ❌ Won't catch async errors
try {
  setTimeout(() => {
    throw new Error('Won\'t be caught!');
  }, 1000);
} catch (error) {
  // Never reaches here
}

Conclusion

Use try/catch/finally for synchronous error handling. Always log errors, clean up resources, and remember it only works for sync code.

#javascript #error-handling #best-practices