Learn async/await error handling in JavaScript with try/catch. Runnable examples covering success, failure, multiple awaits, and rethrowing patterns.
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.
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.
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.
A finally block runs after try (whether it completed or threw) and after catch. Perfect for cleanup — closing connections, releasing locks, hiding spinners.
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.
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.
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.
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.
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.
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.
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.