Async/Await

Node.js Fundamentals — Async/Await
Node.js Fundamentals
Course 1 · Chapter 4 · Async/Await

⏳ Async/Await

async/await is syntactic sugar built on top of promises. It lets you write asynchronous code that looks and feels like synchronous code — no more chains of .then() calls. This makes async code cleaner, easier to read, and easier to debug. This chapter covers async functions, await expressions, error handling with try/catch, and concurrent operations with async/await.

🔧 Async Functions

An async function always returns a promise:

async function greet() { return 'Hello!'; } // This is equivalent to: function greet() { return Promise.resolve('Hello!'); } // Both return a promise greet().then(msg => console.log(msg)); // Hello!

The value you return from an async function is automatically wrapped in a resolved promise.

⏸️ The await Keyword

await pauses execution until a promise settles. It can only be used inside an async function:

async function fetchUser(id) { // Pause here until promise resolves const user = await getUserFromDB(id); console.log('User:', user); return user; } // Call the async function const result = fetchUser(1); // Returns a promise result.then(user => console.log(user));

📊 Async/Await vs Promises

Side-by-Side Comparison

Promise Chains
fetchUser(1) .then(user => { console.log(user); return fetchOrders(user.id); }) .then(orders => { console.log(orders); }) .catch(error => { console.error(error); });
Async/Await
async function load() { try { const user = await fetchUser(1); console.log(user); const orders = await fetchOrders(user.id); console.log(orders); } catch (error) { console.error(error); } } load();

Async/await reads like synchronous code, making it much easier to follow.

🛡️ Error Handling with Try/Catch

Use try/catch to handle errors in async functions:

async function fetchUserData(id) { try { const user = await fetchUser(id); const orders = await fetchOrders(user.id); return { user, orders }; } catch (error) { console.error('Failed to load data:', error.message); return null; // Return fallback value } } // Call it await fetchUserData(1);

If any await expression rejects, execution jumps to catch.

⚡ Sequential vs Concurrent Execution

Sequential (Slow)

async function sequential() { const user = await fetchUser(1); // 1000ms const posts = await fetchPosts(user.id); // 1000ms return { user, posts }; // Total: 2000ms }

Concurrent (Fast)

async function concurrent() { // Start both promises, then await results const userPromise = fetchUser(1); // Start here const postsPromise = fetchPosts(1); // Start here // Wait for both const user = await userPromise; const posts = await postsPromise; return { user, posts }; // Total: ~1000ms } // Or use Promise.all(): async function concurrent() { const [user, posts] = await Promise.all([ fetchUser(1), fetchPosts(1) ]); return { user, posts }; }

🎯 Arrow Async Functions

const fetchAndLog = async (id) => { const user = await fetchUser(id); console.log(user); }; // Usage await fetchAndLog(1);

🚀 Top-Level Await (Modern Node.js)

In recent Node.js versions, you can use await at the top level of a module (no async function needed):

// Old way: wrap in async function async function main() { const data = await fetchData(); console.log(data); } main(); // New way: use await directly const data = await fetchData(); console.log(data);

💻 Coding Challenges

Challenge 1: Convert Promise Chain to Async/Await

Take a promise chain and rewrite it using async/await. Compare readability.

Goal: Practice converting between async patterns.

→ Solution

Challenge 2: Sequential vs Concurrent Operations

Create an async function that fetches three datasets. Implement both sequential and concurrent versions. Measure the time difference.

Goal: Understand performance implications of await placement.

→ Solution

Challenge 3: Error Handling with Try/Catch

Create an async function with multiple awaits and error handling. Show recovery, logging, and finally blocks.

Goal: Master error handling in async code.

→ Solution

💡 Async/Await Best Practices

1. Don't forget await: Forgetting await returns a promise instead of the value.

2. Use concurrent execution: Start independent operations before awaiting them.

3. Always handle errors: Use try/catch or .catch() on the returned promise.

4. Avoid await in loops if they're independent: Use Promise.all() instead.

🎯 What's Next

Now that you can handle asynchronous code cleanly with async/await, we'll explore File System Operations — reading, writing, and managing files in Node.js using these async patterns.