Error Patterns

Error handling patterns are reusable strategies — such as try / catch , custom error classes, and centralized handlers — for catching, classifying, and recovering from failures so large JavaScript apps stay reliable.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Master professional error handling strategies for enterprise-scale applications

What You'll Learn

💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:

Error handling in small scripts is simple: wrap things in try/catch and log a message. But once you start building real applications — dashboards, ecommerce sites, admin tools, mobile apps, AI tools — everything becomes more complex.

Why Error Handling Changes in Big Apps

Pattern 1 — Layered try/catch

Instead of wrapping EVERYTHING in try/catch, you wrap logical boundaries.

Pattern 2 — Centralized Error Handler

Instead of writing console.error everywhere, use a shared handler.

Pattern 3 — Defensive Programming

Instead of assuming everything exists, verify it.

Pattern 4 — Guarding Async Code

❌ Wrong

✔ Right

Even better — wrap async functions in a helper:

Pattern 5 — Global Promise Rejection Handler

Large apps must handle unhandled Promise rejections:

Pattern 6 — Error Boundaries (React)

Pattern 7 — Custom Error Classes

Pattern 8 — Retry Logic with Backoff

Pattern 9 — AbortController for Timeouts

Pattern 10 — Circuit Breaker

If an API repeatedly fails, the app temporarily stops calling it:

What You Learned

Practice quiz

Why does the lesson recommend centralized error handling for big apps?

  • It makes the code shorter only
  • It removes the need for try/catch entirely
  • So problems don't crash the UI or silently break features
  • It speeds up network requests

Answer: So problems don't crash the UI or silently break features. Large apps use structured, centralized patterns so a single missing await or rejected Promise doesn't crash the UI or silently break things.

What is the idea behind 'layered try/catch'?

  • Wrap logical boundaries instead of wrapping everything
  • Wrap absolutely everything in try/catch
  • Never use try/catch
  • Only catch errors in the UI layer

Answer: Wrap logical boundaries instead of wrapping everything. Instead of wrapping every line, you wrap logical boundaries (e.g. initializeApp) so a failure falls back to a controlled error screen.

What is the benefit of a centralized error handler function?

  • It hides all errors from developers
  • It makes errors disappear
  • It only works in React
  • It produces consistent debugging info and prevents duplicated code

Answer: It produces consistent debugging info and prevents duplicated code. A shared handler like handleError(error, context) gives consistent debugging info and avoids scattered console.error calls.

What does defensive programming encourage?

  • Assume everything exists
  • Verify things exist before using them and fail early
  • Catch errors only at the top level
  • Avoid checking input types

Answer: Verify things exist before using them and fail early. Defensive programming verifies elements, API responses, and input types exist (failing early) so small bugs don't become runtime crashes.

In the safe() async helper, what does it return?

The safe(fn) helper returns a [data, error] tuple so you can handle errors without try/catch everywhere.

Which event lets a large app handle unhandled Promise rejections globally?

  • window.addEventListener('error')
  • process.on('exit')
  • window.addEventListener('unhandledrejection')
  • window.onload

Answer: window.addEventListener('unhandledrejection'). Listening for 'unhandledrejection' catches API failures, library issues, and missing .catch() chains across the app.

What do React Error Boundaries allow?

  • Faster rendering of all components
  • Components to fail without breaking the entire UI
  • Automatic retries of failed fetches
  • Global Promise handling

Answer: Components to fail without breaking the entire UI. Error boundaries use componentDidCatch to render a fallback UI, letting a component fail without breaking the whole interface.

Why create custom error classes like ApiError and ValidationError?

  • To make errors throw faster
  • To avoid using try/catch
  • To hide error messages
  • To distinguish error types and catch specific kinds with instanceof

Answer: To distinguish error types and catch specific kinds with instanceof. Custom error classes let you distinguish API errors from UI errors and catch specific kinds (e.g. err instanceof ApiError).

How does exponential backoff change the wait time between retries?

  • It keeps the delay constant
  • It doubles the wait each retry (500ms then 1s then 2s)
  • It halves the delay each time
  • It waits a random amount with no pattern

Answer: It doubles the wait each retry (500ms then 1s then 2s). The retry helper uses delay * 2, doubling the wait between attempts (500ms then 1s then 2s) to prevent server overload.

What does the Circuit Breaker pattern do when an API repeatedly fails?

  • Retries forever
  • Deletes the API endpoint
  • Temporarily stops calling it by opening the circuit
  • Switches to a different programming language

Answer: Temporarily stops calling it by opening the circuit. When failures hit the limit, the breaker sets state to OPEN and temporarily stops calls, resetting to CLOSED after resetTime.