JavaScript Async/Await Error Handling — Try/Catch Examples | TryJS

Learn async/await error handling in JavaScript with try/catch. Runnable examples covering success, failure, multiple awaits, and rethrowing patterns.

Overview

async/await is syntactic sugar over promises, and so is its error handling: a rejected promise becomes a thrown exception inside the async function, catchable with try/catch just like synchronous errors. The catch block receives whatever the rejected promise was rejected with — usually an Error instance, but technically any value. Combined with await, this gives async code the same clean error-handling shape as synchronous code.

How It Works

await turns rejection into throw

When you await a promise that rejects, the await expression throws synchronously inside the async function. Wrapping it in try/catch catches the error the same way you'd catch a regular throw.

One try, multiple awaits

You can put several awaits inside the same try block. The first one that rejects jumps to catch — subsequent awaits are skipped. Use separate try blocks only when each error needs different handling.

finally runs no matter what

A finally block runs after try (whether it completed or threw) and after catch. Perfect for cleanup — closing connections, releasing locks, hiding spinners.

Unhandled rejections still happen

If you don't await a promise and it rejects, you get an unhandledRejection. Always await promises you expect to potentially fail, or attach a .catch(). Silent rejections are the async equivalent of silently-swallowed exceptions.

Common Mistakes

When to Use It

Always handle expected failure modes with try/catch (network errors, missing data, permission denied). Let truly unexpected errors bubble up to a higher-level handler — don't catch errors you don't know how to handle.

Runnable Example

async function fetchData(shouldFail) {
  await new Promise(r => setTimeout(r, 100));
  if (shouldFail) throw new Error("Network error");
  return { data: [1, 2, 3] };
}

(async () => {
  // Success
  try {
    const result = await fetchData(false);
    console.log("Success:", result);
  } catch (err) {
    console.error("Failed:", err.message);
  } finally {
    console.log("cleanup 1");
  }

  // Failure
  try {
    const result = await fetchData(true);
    console.log("Success:", result);
  } catch (err) {
    console.error("Failed:", err.message);
  } finally {
    console.log("cleanup 2");
  }
})();

Open this example in the TryJS playground to edit and run the code instantly in your browser — no signup needed.

Frequently Asked Questions

How do I handle errors in async/await?

Wrap the await expression in a try/catch block. A rejected promise becomes a thrown exception inside the async function, so try/catch catches it exactly like a synchronous throw.

Can I use try/catch with multiple awaits?

Yes. A single try block can wrap as many awaits as you want. The first rejection jumps to the catch block, and subsequent awaits are skipped. Use separate try blocks only when each error requires different handling.

What is an unhandled promise rejection?

A promise that rejects and has no catch handler or try/catch wrapping an await. Node and browsers emit an 'unhandledRejection' event; in strict environments it terminates the process. Always await or .catch() every promise you create.

Should I always use try/catch inside async functions?

Only at layers where you can handle the error — retry, show a user-facing message, log it, fall back to a default. Don't wrap every line in try/catch 'just in case'; let errors bubble up to a layer that knows what to do.