AiTechWorlds
AiTechWorlds
University learning companion
JavaScript Complete 2026 · async/await: Clean Async Code
Use this course-level map to place “async/await: Clean Async Code” in the larger system. It complements—not replaces—a topic-specific worked diagram.
async/await transformed how JavaScript developers write asynchronous code. Instead of Promise chains, you write code that looks and reads like synchronous code while still being fully non blocking.
// An async function always returns a Promise
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user; // Automatically wrapped in Promise.resolve()
}
// await pauses execution until the Promise resolves
// But it only pauses the async function — the rest of the JS thread keeps running// Using the function
fetchUser(1).then(user => console.log(user));
// Or with await in another async function
async function main() {
const user = await fetchUser(1);
console.log(user.name);
}async function getWeather(city) {
try {
const response = await fetch(`https://api.weather.com/current?city=${city}`);
if (!response.ok) {
throw new Error(`API returned ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error(`Failed to get weather for ${city}:`, error.message);
return null; // Return fallback instead of crashing
}
}This is cleaner than .then().catch() chains, especially when you have multiple async operations that each might fail.
This is the most important performance concept with async/await:
// SEQUENTIAL — each waits for the previous to complete (~3 seconds total)
async function loadDashboard_slow() {
const user = await fetchUser(1); // 1 second
const orders = await fetchOrders(1); // 1 second
const analytics = await fetchAnalytics(1); // 1 second
return { user, orders, analytics };
}
// PARALLEL — all start at the same time (~1 second total)
async function loadDashboard_fast() {
const [user, orders, analytics] = await Promise.all([
fetchUser(1),
fetchOrders(1),
fetchAnalytics(1),
]);
return { user, orders, analytics };
}Rule: If requests don't depend on each other, run them in parallel with Promise.all. Sequential is for when you need the result of one to make the next request.
async function fetchWithHandling(url) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5s timeout
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out after 5 seconds');
}
throw error;
} finally {
clearTimeout(timeoutId);
}
}async function fetchWithRetry(url, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = attempt * 1000; // Exponential backoff: 1s, 2s, 3s
console.log(`Attempt ${attempt} failed. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}// Process items one at a time (useful to avoid rate limits)
async function processItems(items) {
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
return results;
}
// Process items in batches of N (parallel within batch, sequential between)
async function processInBatches(items, batchSize = 5) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(processItem));
results.push(...batchResults);
}
return results;
}// React: fetch data on component mount
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function loadUser() {
try {
setLoading(true);
const data = await fetchUser(userId);
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
loadUser();
}, [userId]);
if (loading) return <Spinner />;
if (error) return <Error message={error} />;
return <UserCard user={user} />;
}// BUG: data is a Promise, not the actual data
async function getUser(id) {
const data = fetch(`/api/users/${id}`).then(r => r.json()); // forgot await
console.log(data); // Promise { <pending> }
}
// FIX:
async function getUser(id) {
const data = await fetch(`/api/users/${id}`).then(r => r.json());
console.log(data); // The actual user object
}// SLOW: these could run in parallel
const a = await getA();
const b = await getB(); // b waits for a, even though it doesn't need to
// FAST:
const [a, b] = await Promise.all([getA(), getB()]);const ids = [1, 2, 3, 4, 5];
// BUG: forEach doesn't wait for async callbacks
ids.forEach(async (id) => {
await processId(id); // These all run concurrently but forEach returns before they complete
});
// FIX option 1: for...of (sequential)
for (const id of ids) {
await processId(id);
}
// FIX option 2: Promise.all (parallel)
await Promise.all(ids.map(id => processId(id)));Use async/await for:
Use Promises directly for:
Promise.all(), Promise.race(), Promise.allSettled()They work together seamlessly await works on any Promise.
Next lesson: Fetch API & Working with REST APIs building real data driven applications.
University learning companion
JavaScript Complete 2026 · async/await: Clean Async Code
Complete the steps without looking at the answer first; record evidence rather than only a final result.
These are primary or established learning references for this subject. Check the relevant chapter or specification for the exact topic and version.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises