Async error handling
| Throw inside async | |
| Catch per call | |
| Top-level guard | |
| Retry with backoff sketch |
Throw inside async
A throw inside an async function rejects the returned promise. The caller sees the same error through await or catch. This lets async code use plain throw instead of manual reject calls.
async function load(): Promise<string> {
throw new Error("missing");
}
load().catch((err: unknown) => {
console.log(err);
});
Catch per call
Handle each risky await where recovery is possible. A small try block around one call keeps the fix close to the cause. Return a default or rethrow when the caller must decide.
async function withDefault(): Promise<string> {
try {
return await load();
} catch (err: unknown) {
console.log(err);
return "guest";
}
}
Top-level guard
Every program needs one last guard that logs fatal errors. Attach catch to the top promise or wrap main in try and catch. Without it a rejection becomes an unhandled crash with a thin trace.
async function main(): Promise<void> {
const name = await withDefault();
console.log(name);
}
main().catch((err: unknown) => {
console.log(err);
});
Retry with backoff sketch
Transient faults often clear on a second try after a short pause. Retry a few times with growing delays between attempts. Rethrow the last error when attempts run out so the guard above still sees it.
function delay(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function retry(tries: number): Promise<string> {
let last: unknown = null;
for (let i = 0; i < tries; i++) {
try {
return await load();
} catch (err: unknown) {
last = err;
await delay(100 * (i + 1));
}
}
throw last;
}
Next: Sequential vs parallel
Article author: Arthur Isaev