Async/Await & Promises in Node.js
Asynchronous programming is at the heart of Node.js. Mastering Promises and async/await is essential for writing clean, efficient, non-blocking server code.
1 Callbacks vs Promises vs Async/Await
The evolution of async code in Node.js:
JavaScript — Three Async Styles
// 1. Callback style (older, leads to "callback hell")
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) return console.error(err);
console.log(data);
});
// 2. Promise style
fs.promises.readFile('data.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
// 3. Async/Await style (modern, recommended)
async function readData() {
try {
const data = await fs.promises.readFile('data.txt', 'utf8');
console.log(data);
} catch (err) {
console.error('Failed to read:', err.message);
}
}
readData();
2 Parallel & Sequential Async Operations
JavaScript — Promise.all & Sequential
// Sequential — each waits for the previous to finish
async function sequential() {
const user = await fetchUser(1);
const orders = await fetchOrders(user.id); // waits for user first
const invoice = await fetchInvoice(orders[0]);
return { user, orders, invoice };
}
// Parallel — all fire at the same time (much faster!)
async function parallel() {
const [users, products, stats] = await Promise.all([
fetchUsers(),
fetchProducts(),
fetchStats()
]);
return { users, products, stats };
}
// Promise.allSettled — continues even if some promises fail
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(999), // might fail
fetchUser(3)
]);
results.forEach(r => {
if (r.status === 'fulfilled') console.log('Got:', r.value);
else console.log('Failed:', r.reason.message);
});
3 Code Challenge
Challenge: Write an async function that fetches data from three different public API endpoints simultaneously using
Promise.all and the built-in fetch API (Node 18+). Display the combined results.