Async/Await & Promises in Node.js

🌿 Node.js Lesson 8 Intermediate

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.