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.
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.
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.
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.