From Callbacks to Async/Await
The evolution of JavaScript async code.
Callback Hell
getUser(id, (err, user) => {
if (err) return handleError(err);
getOrders(user.id, (err, orders) => {
if (err) return handleError(err);
getProducts(orders[0].id, (err, products) => {
// Deep nesting nightmare
});
});
});
Promise Chains
getUser(id)
.then(user => getOrders(user.id))
.then(orders => getProducts(orders[0].id))
.then(products => console.log(products))
.catch(handleError);
Async/Await
async function loadData(id) {
try {
const user = await getUser(id);
const orders = await getOrders(user.id);
const products = await getProducts(orders[0].id);
return products;
} catch (error) {
handleError(error);
}
}
Parallel Execution
// Sequential (slow)
const users = await getUsers();
const posts = await getPosts();
const comments = await getComments();
// Parallel (fast)
const [users, posts, comments] = await Promise.all([
getUsers(),
getPosts(),
getComments()
]);
Promise.allSettled
When you need all results, even failures:
const results = await Promise.allSettled([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.log('Failed:', result.reason);
}
});
Racing Promises
First one wins:
const result = await Promise.race([
fetch('/api/data'),
timeout(5000) // Reject after 5s
]);
Common Mistakes
// Forgetting await
const user = getUser(id); // user is a Promise, not data
// Await in loops (sequential when parallel is possible)
for (const id of ids) {
await processItem(id); // Slow
}
// Better
await Promise.all(ids.map(id => processItem(id))); // Parallel
