Lesson 26 of 30

Error Handling

Managing Errors Gracefully

Error handling allows your program to gracefully handle unexpected situations instead of crashing or showing cryptic error messages to users.

JavaScript provides try/catch/finally blocks to catch and handle errors, and you can create custom error types for specific situations.

Proper error handling improves user experience, helps with debugging, and makes your applications more robust and production-ready.

Example
// Basic try/catch
try {
  const result = riskyOperation();
  console.log(result);
} catch (error) {
  console.error('An error occurred:', error.message);
}

// try/catch/finally
try {
  const data = JSON.parse(userInput);
  processData(data);
} catch (error) {
  console.error('Error processing data:', error);
  showErrorMessage('Invalid data format');
} finally {
  console.log('Cleanup code runs regardless of error');
  hideLoadingSpinner();
}

// Throwing custom errors
function divide(a, b) {
  if (b === 0) {
    throw new Error('Cannot divide by zero!');
  }
  return a / b;
}

// Custom error types
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

function validateEmail(email) {
  if (!email.includes('@')) {
    throw new ValidationError('Invalid email format');
  }
  return true;
}

try {
  validateEmail('notanemail');
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Validation failed:', error.message);
  } else {
    console.log('Unknown error:', error);
  }
}
  • try block - Code that might throw an error
  • catch block - Handles errors if they occur
  • finally block - Runs regardless of error (cleanup code)
  • throw - Manually throw an error
  • Error object - Built-in object with name and message properties
  • Custom Errors - Extend Error class for specific error types
  • error.stack - Stack trace showing where error occurred
  • Multiple catches - Handle different error types differently
Try Error Handling
HTML
<div class="error-demo">
  <h3>Error Handling Examples</h3>

  <div class="example">
    <h4>1. Try/Catch with JSON</h4>
    <textarea id="jsonInput" rows="3">{"name": "Valid JSON"}</textarea>
    <button id="parseBtn">Parse JSON</button>
    <div id="parseResult"></div>
  </div>

  <div class="example">
    <h4>2. Custom Error - Division Calculator</h4>
    <input type="number" id="num1" placeholder="Numerator" value="10">
    <input type="number" id="num2" placeholder="Denominator" value="2">
    <button id="divideBtn">Divide</button>
    <div id="divideResult"></div>
  </div>

  <div class="example">
    <h4>3. Form Validation with Custom Errors</h4>
    <input type="text" id="emailInput" placeholder="Enter email">
    <input type="number" id="ageInput" placeholder="Age (13-120)">
    <button id="validateBtn">Validate</button>
    <div id="validateResult"></div>
  </div>
</div>
CSS
.error-demo {
  padding: 20px;
  max-width: 700px;
}

.example {
  margin: 20px 0;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 8px;
}

h4 {
  margin-top: 0;
  color: #333;
}

textarea {
  width: 100%;
  padding: 10px;
  font-family: 'Courier New', monospace;
  font-size: 13px;
  border: 2px solid #ddd;
  border-radius: 4px;
  box-sizing: border-box;
  resize: vertical;
}

input {
  width: calc(50% - 6px);
  padding: 10px;
  margin: 5px 2px;
  border: 2px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

button {
  width: 100%;
  padding: 10px;
  margin: 10px 0;
  background: #2196F3;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

button:hover {
  background: #1976D2;
}

#parseResult, #divideResult, #validateResult {
  margin-top: 10px;
  padding: 12px;
  background: white;
  border: 2px solid #ddd;
  border-radius: 4px;
  font-family: 'Courier New', monospace;
  font-size: 13px;
  min-height: 40px;
}

.success {
  border-color: #4CAF50 !important;
  background-color: #e8f5e9 !important;
  color: #2e7d32;
}

.error {
  border-color: #f44336 !important;
  background-color: #ffebee !important;
  color: #c62828;
}
JavaScript
// Example 1: Try/Catch with JSON Parsing
document.getElementById('parseBtn').addEventListener('click', function() {
  const input = document.getElementById('jsonInput').value;
  const result = document.getElementById('parseResult');

  try {
    const parsed = JSON.parse(input);
    result.className = 'success';
    result.innerHTML = '<strong>✓ Valid JSON!</strong><br><br>' +
      'Parsed object:<br>' +
      JSON.stringify(parsed, null, 2);
  } catch (error) {
    result.className = 'error';
    result.innerHTML = '<strong>✗ Parse Error!</strong><br><br>' +
      '<strong>Error:</strong> ' + error.message + '<br><br>' +
      '<strong>Tip:</strong> Check for missing quotes, commas, or brackets';
  }
});

// Example 2: Custom Error - Division
function safeDivide(a, b) {
  if (b === 0) {
    throw new Error('Division by zero is not allowed!');
  }
  if (isNaN(a) || isNaN(b)) {
    throw new Error('Both inputs must be valid numbers!');
  }
  return a / b;
}

document.getElementById('divideBtn').addEventListener('click', function() {
  const num1 = parseFloat(document.getElementById('num1').value);
  const num2 = parseFloat(document.getElementById('num2').value);
  const result = document.getElementById('divideResult');

  try {
    const answer = safeDivide(num1, num2);
    result.className = 'success';
    result.innerHTML = `<strong>✓ Success!</strong><br><br>` +
      `${num1} ÷ ${num2} = ${answer}`;
  } catch (error) {
    result.className = 'error';
    result.innerHTML = '<strong>✗ Error!</strong><br><br>' + error.message;
  }
});

// Example 3: Custom Error Classes
class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;
  }
}

function validateForm(email, age) {
  const errors = [];

  // Email validation
  if (!email || email.trim() === '') {
    throw new ValidationError('email', 'Email is required');
  }
  if (!email.includes('@') || !email.includes('.')) {
    throw new ValidationError('email', 'Invalid email format');
  }

  // Age validation
  if (!age || isNaN(age)) {
    throw new ValidationError('age', 'Age must be a number');
  }
  if (age < 13 || age > 120) {
    throw new ValidationError('age', 'Age must be between 13 and 120');
  }

  return true;
}

document.getElementById('validateBtn').addEventListener('click', function() {
  const email = document.getElementById('emailInput').value;
  const age = parseInt(document.getElementById('ageInput').value);
  const result = document.getElementById('validateResult');

  try {
    validateForm(email, age);
    result.className = 'success';
    result.innerHTML = '<strong>✓ Validation Successful!</strong><br><br>' +
      `Email: ${email}<br>` +
      `Age: ${age}`;
  } catch (error) {
    result.className = 'error';

    if (error instanceof ValidationError) {
      result.innerHTML = '<strong>✗ Validation Error!</strong><br><br>' +
        `<strong>Field:</strong> ${error.field}<br>` +
        `<strong>Error:</strong> ${error.message}`;
    } else {
      result.innerHTML = '<strong>✗ Unexpected Error!</strong><br><br>' +
        error.message;
    }
  }
});
Notes
  • Don't Swallow Errors: Always log or handle errors appropriately - silent failures make debugging very difficult.
  • User-Friendly Messages: Show clear, helpful error messages to users while logging technical details for developers.
  • Finally Block: Use finally for cleanup code that must run (closing connections, hiding loaders) regardless of errors.