JavaScript Async/Await Patterns
Write asynchronous code that looks synchronous.
Basic Async/Await
async function fetchData() {
let response = await fetch("/api/data");
let data = await response.json();
return data;
}
fetchData().then(data => console.log(data));
Error Handling
async function getData() {
try {
let response = await fetch("/api/data");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
console.error("Error:", error.message);
return null;
}
}
Sequential vs Parallel
Sequential — when each step depends on the previous result:
async function sequential() {
let user = await fetchUser();
let posts = await fetchPosts(user.id);
return { user, posts };
}
Parallel — when operations are independent:
async function parallel() {
let [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
return { user, posts };
}
Remember: Parallel is faster when operations don’t depend on each other.
API Service Example
class ApiService {
#baseUrl;
#cache = new Map();
constructor(baseUrl) { this.#baseUrl = baseUrl; }
async get(endpoint) {
let url = `${this.#baseUrl}${endpoint}`;
if (this.#cache.has(url)) return this.#cache.get(url);
let response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
let data = await response.json();
this.#cache.set(url, data);
return data;
}
async post(endpoint, body) {
let response = await fetch(`${this.#baseUrl}${endpoint}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
}
}
Batch Processing
async function batchProcess(items, batchSize = 10) {
let results = [];
for (let i = 0; i < items.length; i += batchSize) {
let batch = items.slice(i, i + batchSize);
let batchResults = await Promise.all(batch.map(item => processItem(item)));
results.push(...batchResults);
}
return results;
}
Retry and Timeout
function withRetry(fn, maxAttempts = 3, delay = 1000) {
return async function(...args) {
for (let i = 1; i <= maxAttempts; i++) {
try { return await fn(...args); }
catch (error) {
if (i === maxAttempts) throw error;
await new Promise(r => setTimeout(r, delay));
}
}
};
}
function withTimeout(promise, ms = 5000) {
return Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), ms))
]);
}
Best Practices
- Use async/await — Cleaner than
.then() - Handle errors with try/catch — Always catch errors
- Use
Promise.all()for parallel — Don’t await sequentially - Avoid unnecessary awaits — Don’t await synchronous values
Common Mistakes
- Forgetting to
await— Returns promise instead of value - Missing error handling — Unhandled rejections
- Overusing
await— Sequential when parallel is possible - Awaiting in loops — Use
Promise.all()instead