Async/Await Patterns Every Developer Should Know
Move beyond basic async/await with these practical patterns for error handling, concurrency, and more.
Beyond the Basics
Most developers learn the basic async/await syntax quickly, but there are patterns that separate good async code from great async code.
Pattern 1: Parallel Execution
Don't await sequentially when tasks are independent:
// Slow — sequential
const users = await fetchUsers();
const posts = await fetchPosts();
// Fast — parallel
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts(),
]);
Pattern 2: Error Handling Wrapper
Create a utility to avoid repetitive try-catch blocks:
async function tryCatch(promise) {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
const [user, error] = await tryCatch(fetchUser(id));
if (error) console.error('Failed:', error.message);
Pattern 3: Retry Logic
async function retry(fn, attempts = 3, delay = 1000) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
await new Promise((r) => setTimeout(r, delay * (i + 1)));
}
}
}
const data = await retry(() => fetch('/api/data'));
These patterns will make your async code more robust and readable.