Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Error Handling in JavaScript

Error Handling in JavaScript

~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

To wrap up phase 4, let's build robust error handling for our entire file system – including our own, meaningful error classes for the budget app.

try, catch, and finally

try {
  const data = JSON.parse('{ invalid json');
  console.log(data);
} catch (error) {
  console.error('Error parsing:', error.message);
} finally {
  console.log('Always runs - error or not'); // e.g. for cleanup work
}

try/catch with async/await

The big practical advantage of async/await over .then()/.catch() chains: error handling uses the SAME try/catch as synchronous code, instead of needing a separate .catch() syntax:

async function loadTransactionsSafely(path) {
  try {
    const content = await readFile(path, 'utf-8');
    return JSON.parse(content);
  } catch (error) {
    console.error(`Could not load ${path}:`, error.message);
    return []; // a sensible fallback instead of crashing
  }
}

Custom error classes

The built-in Error class can be extended to model MORE MEANINGFUL, specific error types for our budget app:

src/errors.js
export class BudgetAppError extends Error {
  constructor(message) {
    super(message);
    this.name = 'BudgetAppError'; // appears in stack traces instead of 'Error'
  }
}

export class InvalidTransactionError extends BudgetAppError {
  constructor(reason) {
    super(`Invalid transaction: ${reason}`);
    this.name = 'InvalidTransactionError';
  }
}

export class FileNotFoundError extends BudgetAppError {
  constructor(path) {
    super(`File not found: ${path}`);
    this.name = 'FileNotFoundError';
    this.path = path; // additional, specific information on the error
  }
}

Distinguishing error types in a catch block

import { InvalidTransactionError, FileNotFoundError } from './errors.js';

function validateTransaction(transaction) {
  if (typeof transaction.amount !== 'number' || Number.isNaN(transaction.amount)) {
    throw new InvalidTransactionError('Amount must be a valid number');
  }
  if (!transaction.description) {
    throw new InvalidTransactionError('Description is missing');
  }
}

try {
  validateTransaction({ description: 'Rent', amount: 'not-a-number' });
} catch (error) {
  if (error instanceof InvalidTransactionError) {
    console.error('Validation error:', error.message);
  } else if (error instanceof FileNotFoundError) {
    console.error('File problem:', error.path);
  } else {
    throw error; // unknown error type - rethrow instead of swallowing it!
  }
}

Achtung: instanceof uses the prototype chain from chapter 20 – WHICH is why this distinction works reliably even across several inheritance levels (InvalidTransactionErrorBudgetAppErrorError). SILENTLY ignoring an unknown error type in a catch block, instead of rethrowing it, hides real bugs.

The complete file storage system

Let's now combine everything from phase 4 into a robust storage system for our budget app:

src/fileStorage.js
import { readFile, writeFile } from 'node:fs/promises';
import { FileNotFoundError, BudgetAppError } from './errors.js';

export async function loadTransactions(path) {
  try {
    const content = await readFile(path, 'utf-8');
    return JSON.parse(content);
  } catch (error) {
    if (error.code === 'ENOENT') {
      throw new FileNotFoundError(path);
    }
    throw new BudgetAppError(`Could not read ${path}: ${error.message}`);
  }
}

export async function saveTransactions(path, transactions) {
  try {
    await writeFile(path, JSON.stringify(transactions, null, 2), 'utf-8');
  } catch (error) {
    throw new BudgetAppError(`Could not write ${path}: ${error.message}`);
  }
}
src/index.js
import { loadTransactions, saveTransactions } from './fileStorage.js';
import { FileNotFoundError } from './errors.js';

try {
  const transactions = await loadTransactions('data/transactions.json');
  console.log(`${transactions.length} transactions loaded`);

  transactions.push({ description: 'Movies', amount: -18, category: 'Leisure' });
  await saveTransactions('data/transactions.json', transactions);
  console.log('Saved!');
} catch (error) {
  if (error instanceof FileNotFoundError) {
    console.log('No file yet, starting with an empty budget.');
  } else {
    console.error('Unexpected error:', error.message);
  }
}

With that, phase 4 (asynchrony) is complete! In phase 5, we round off the project with a deeper look at JSON persistence, automated testing, and a final best-practices wrap-up.