javascript

examples

examples.js
/**
 * =====================================================
 * 4.6 ERROR HANDLING WITH TRY-CATCH - EXAMPLES
 * =====================================================
 * Graceful error handling
 */

// =====================================================
// 1. BASIC TRY-CATCH
// =====================================================

console.log('--- Basic try-catch ---');

try {
  const result = JSON.parse('{"name": "John"}');
  console.log('Parsed:', result);
} catch (error) {
  console.log('Error:', error.message);
}

// Without try-catch, this would crash
try {
  const invalid = JSON.parse('not valid json');
} catch (error) {
  console.log('Failed to parse:', error.message);
}

console.log('Program continues after error handling...');

// =====================================================
// 2. ERROR OBJECT PROPERTIES
// =====================================================

console.log('\n--- Error Object Properties ---');

try {
  undefined.property;
} catch (error) {
  console.log('Name:', error.name);
  console.log('Message:', error.message);
  console.log('Stack (first 100 chars):', error.stack?.substring(0, 100));
}

// =====================================================
// 3. THE FINALLY BLOCK
// =====================================================

console.log('\n--- Finally Block ---');

function exampleWithFinally(shouldFail) {
  console.log(`\nRunning with shouldFail = ${shouldFail}`);
  try {
    console.log('  Try block executing...');
    if (shouldFail) {
      throw new Error('Intentional error');
    }
    console.log('  Try block completed successfully');
  } catch (error) {
    console.log('  Catch block:', error.message);
  } finally {
    console.log('  Finally block: Always runs!');
  }
}

exampleWithFinally(false);
exampleWithFinally(true);

// =====================================================
// 4. FINALLY WITH RETURN
// =====================================================

console.log('\n--- Finally with Return ---');

function demonstrateFinally() {
  try {
    console.log('Try block');
    return 'Return from try';
  } finally {
    console.log('Finally block executes before return');
  }
}

console.log('Result:', demonstrateFinally());

// =====================================================
// 5. THROWING ERRORS
// =====================================================

console.log('\n--- Throwing Errors ---');

function divide(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    throw new TypeError('Both arguments must be numbers');
  }
  if (b === 0) {
    throw new Error('Division by zero');
  }
  return a / b;
}

try {
  console.log('10 / 2 =', divide(10, 2));
  console.log('10 / 0 =', divide(10, 0));
} catch (error) {
  console.log('Error:', error.message);
}

try {
  console.log("'10' / 2 =", divide('10', 2));
} catch (error) {
  console.log('Error:', error.name, '-', error.message);
}

// =====================================================
// 6. ERROR TYPES
// =====================================================

console.log('\n--- Error Types ---');

// TypeError
try {
  null.property;
} catch (error) {
  console.log('TypeError caught:', error.message);
}

// RangeError
try {
  const arr = new Array(-1);
} catch (error) {
  console.log('RangeError caught:', error.message);
}

// SyntaxError (via JSON.parse)
try {
  JSON.parse('{invalid}');
} catch (error) {
  console.log('SyntaxError caught:', error.message);
}

// URIError
try {
  decodeURIComponent('%');
} catch (error) {
  console.log('URIError caught:', error.message);
}

// =====================================================
// 7. CHECKING ERROR TYPE
// =====================================================

console.log('\n--- Checking Error Type ---');

function handleError(error) {
  if (error instanceof TypeError) {
    console.log('Type Error:', error.message);
  } else if (error instanceof RangeError) {
    console.log('Range Error:', error.message);
  } else if (error instanceof SyntaxError) {
    console.log('Syntax Error:', error.message);
  } else {
    console.log('Other Error:', error.message);
  }
}

try {
  null.x;
} catch (e) {
  handleError(e);
}
try {
  new Array(-1);
} catch (e) {
  handleError(e);
}
try {
  JSON.parse('{');
} catch (e) {
  handleError(e);
}
try {
  throw new Error('custom');
} catch (e) {
  handleError(e);
}

// =====================================================
// 8. CUSTOM ERROR CLASSES
// =====================================================

console.log('\n--- Custom Error Classes ---');

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

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

function validateUser(user) {
  if (!user.name) {
    throw new ValidationError('Name is required', 'name');
  }
  if (!user.email) {
    throw new ValidationError('Email is required', 'email');
  }
  if (!user.email.includes('@')) {
    throw new ValidationError('Invalid email format', 'email');
  }
  return true;
}

try {
  validateUser({ name: 'John' });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`Validation Error on field '${error.field}': ${error.message}`);
  }
}

// =====================================================
// 9. RE-THROWING ERRORS
// =====================================================

console.log('\n--- Re-throwing Errors ---');

function processJSON(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    console.log('Logging error:', error.message);
    // Re-throw with more context
    throw new Error(`Failed to parse JSON: ${error.message}`);
  }
}

try {
  const data = processJSON('invalid');
} catch (error) {
  console.log('Final handler:', error.message);
}

// =====================================================
// 10. NESTED TRY-CATCH
// =====================================================

console.log('\n--- Nested try-catch ---');

function outerFunction() {
  try {
    console.log('Outer try');
    innerFunction();
  } catch (error) {
    console.log('Outer catch:', error.message);
  }
}

function innerFunction() {
  try {
    console.log('Inner try');
    throw new Error('Inner error');
  } catch (error) {
    console.log('Inner catch:', error.message);
    // Re-throw
    throw new Error('Wrapped: ' + error.message);
  }
}

outerFunction();

// =====================================================
// 11. DEFAULT VALUE PATTERN
// =====================================================

console.log('\n--- Default Value Pattern ---');

function safeParse(jsonString, defaultValue = null) {
  try {
    return JSON.parse(jsonString);
  } catch {
    return defaultValue;
  }
}

console.log(safeParse('{"valid": true}')); // { valid: true }
console.log(safeParse('invalid')); // null
console.log(safeParse('invalid', {})); // {}
console.log(safeParse('invalid', [])); // []

// =====================================================
// 12. ERROR WRAPPER FUNCTION
// =====================================================

console.log('\n--- Error Wrapper ---');

function tryCatch(fn, errorHandler) {
  return function (...args) {
    try {
      return fn(...args);
    } catch (error) {
      return errorHandler(error);
    }
  };
}

const riskyFunction = (x) => {
  if (x < 0) throw new Error('Negative number');
  return x * 2;
};

const safeFunction = tryCatch(riskyFunction, (error) => {
  console.log('Handled:', error.message);
  return 0;
});

console.log('Safe(5):', safeFunction(5)); // 10
console.log('Safe(-1):', safeFunction(-1)); // 0 (handled)

// =====================================================
// 13. ERROR AGGREGATION
// =====================================================

console.log('\n--- Error Aggregation ---');

function validateForm(data) {
  const errors = [];

  if (!data.username || data.username.length < 3) {
    errors.push(new Error('Username must be at least 3 characters'));
  }
  if (!data.email || !data.email.includes('@')) {
    errors.push(new Error('Valid email is required'));
  }
  if (!data.password || data.password.length < 8) {
    errors.push(new Error('Password must be at least 8 characters'));
  }

  if (errors.length > 0) {
    // Create AggregateError with all validation errors
    const aggregateError = new AggregateError(
      errors,
      `Form validation failed with ${errors.length} error(s)`
    );
    throw aggregateError;
  }

  return true;
}

try {
  validateForm({ username: 'ab', email: 'invalid', password: '123' });
} catch (error) {
  if (error instanceof AggregateError) {
    console.log('AggregateError:', error.message);
    console.log('Individual errors:');
    for (const e of error.errors) {
      console.log(' -', e.message);
    }
  }
}

// =====================================================
// 14. RESOURCE CLEANUP
// =====================================================

console.log('\n--- Resource Cleanup Pattern ---');

class Database {
  constructor() {
    this.connected = false;
  }

  connect() {
    console.log('Database connected');
    this.connected = true;
  }

  query(sql) {
    if (!this.connected) {
      throw new Error('Not connected');
    }
    if (sql.includes('error')) {
      throw new Error('Query failed');
    }
    return { data: 'result' };
  }

  disconnect() {
    console.log('Database disconnected');
    this.connected = false;
  }
}

function performDatabaseOperation(sql) {
  const db = new Database();

  try {
    db.connect();
    const result = db.query(sql);
    console.log('Query result:', result);
    return result;
  } catch (error) {
    console.log('Database error:', error.message);
    return null;
  } finally {
    db.disconnect(); // Always disconnect
  }
}

console.log('\nSuccessful query:');
performDatabaseOperation('SELECT * FROM users');

console.log('\nFailed query:');
performDatabaseOperation('SELECT error FROM table');

// =====================================================
// 15. PRACTICAL EXAMPLE: API RESPONSE HANDLER
// =====================================================

console.log('\n--- API Response Handler ---');

class APIError extends Error {
  constructor(message, status, data) {
    super(message);
    this.name = 'APIError';
    this.status = status;
    this.data = data;
  }
}

function handleAPIResponse(response) {
  try {
    // Simulate response parsing
    const data = JSON.parse(response);

    if (data.error) {
      throw new APIError(data.error, data.status, data);
    }

    return data;
  } catch (error) {
    if (error instanceof APIError) {
      console.log(`API Error (${error.status}): ${error.message}`);
    } else if (error instanceof SyntaxError) {
      console.log('Invalid JSON response');
    } else {
      console.log('Unexpected error:', error.message);
    }
    return null;
  }
}

// Test different scenarios
console.log('\nValid response:');
console.log(handleAPIResponse('{"success": true, "data": [1, 2, 3]}'));

console.log('\nAPI error:');
console.log(handleAPIResponse('{"error": "Not found", "status": 404}'));

console.log('\nInvalid JSON:');
console.log(handleAPIResponse('not json'));

// =====================================================
// SUMMARY
// =====================================================

console.log('\n--- Summary ---');
console.log(`
Error Handling Basics:
  • try { code } catch (e) { handle } finally { cleanup }
  • throw new Error("message") to throw errors
  • error.name, error.message, error.stack

Error Types:
  • Error - base class
  • TypeError - wrong type
  • RangeError - value out of range
  • SyntaxError - invalid syntax (JSON.parse)
  • ReferenceError - undefined variable
  • URIError - invalid URI encoding

Custom Errors:
  • class CustomError extends Error
  • Add custom properties (code, field, status)
  • Use instanceof to check type

Best Practices:
  • Don't swallow errors silently
  • Use finally for cleanup
  • Include helpful error messages
  • Validate inputs early
  • Re-throw when appropriate
`);
Examples - JavaScript Tutorial | DeepML