Promises and Async/Await
Managing asynchronous operations with Promises and async/await
Challenge
Section titled “Challenge”You need to fetch data from multiple APIs in sequence, then process the results. The old callback approach leads to callback hell. How can you make this clean and readable?
Explanation
Section titled “Explanation”Promises represent a value that may not be available yet but will be resolved eventually. They provide a cleaner alternative to callbacks and are the foundation for async/await syntax.
Promise Fundamentals
Section titled “Promise Fundamentals”// Creating a Promiseconst promise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Success!'); }, 1000);});
promise.then( result => console.log(result), // Success! error => console.log('Error:', error));
// Or using catchpromise .then(result => console.log(result)) .catch(error => console.log('Error:', error));Solution: Sequential API Calls
Section titled “Solution: Sequential API Calls”// ❌ Callback Hellfunction fetchData(callback) { fetch('/api/user').then(res => { res.json().then(user => { fetch(`/api/posts/${user.id}`).then(res => { res.json().then(posts => { fetch(`/api/comments/${posts[0].id}`).then(res => { res.json().then(comments => { callback(user, posts, comments); }); }); }); }); }); });}
// ✅ Using Promisesfunction fetchDataWithPromises() { return fetch('/api/user') .then(res => res.json()) .then(user => { return fetch(`/api/posts/${user.id}`) .then(res => res.json()) .then(posts => ({ user, posts })); }) .then(({ user, posts }) => { return fetch(`/api/comments/${posts[0].id}`) .then(res => res.json()) .then(comments => ({ user, posts, comments })); });}
// ✅ Using Async/Await (Cleanest)async function fetchDataWithAsync() { const userRes = await fetch('/api/user'); const user = await userRes.json();
const postsRes = await fetch(`/api/posts/${user.id}`); const posts = await postsRes.json();
const commentsRes = await fetch(`/api/comments/${posts[0].id}`); const comments = await commentsRes.json();
return { user, posts, comments };}
// UsagefetchDataWithAsync() .then(data => console.log(data)) .catch(error => console.error(error));Promise States and Transitions
Section titled “Promise States and Transitions”// A promise can be in one of three states:
// 1. PENDING: Initial stateconst pending = new Promise(resolve => { // Still waiting...});
// 2. FULFILLED: Resolved with valueconst fulfilled = new Promise(resolve => { resolve('Done!');});
// 3. REJECTED: Rejected with reasonconst rejected = new Promise((resolve, reject) => { reject(new Error('Failed!'));});
// Once settled (fulfilled or rejected), state cannot changeconst promise = new Promise(resolve => { resolve('First'); resolve('Second'); // Ignored - already settled});
promise.then(value => console.log(value)); // "First"Advanced Promise Patterns
Section titled “Advanced Promise Patterns”// Promise.all - Wait for all promisesPromise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/posts').then(r => r.json()), fetch('/api/comments').then(r => r.json())]).then(([user, posts, comments]) => { console.log(user, posts, comments);}).catch(error => console.log('One promise failed:', error));
// Promise.race - First promise winsPromise.race([ fetch('/api/fast'), fetch('/api/slow')]).then(winner => console.log(winner));
// Promise.allSettled - All results regardless of successPromise.allSettled([ Promise.resolve(1), Promise.reject('error'), Promise.resolve(3)]).then(results => { console.log(results); // [ // { status: 'fulfilled', value: 1 }, // { status: 'rejected', reason: 'error' }, // { status: 'fulfilled', value: 3 } // ]});
// Promise.any - First fulfilled promisePromise.any([ Promise.reject('error1'), Promise.resolve('success'), Promise.reject('error2')]).then(winner => console.log(winner)); // "success"Error Handling with Async/Await
Section titled “Error Handling with Async/Await”// Using try/catchasync function fetchUserData() { try { const user = await fetchUser(); const posts = await fetchPosts(user.id); return { user, posts }; } catch (error) { console.error('Failed to fetch data:', error); throw error; // Re-throw if needed } finally { console.log('Cleanup code here'); }}
// Handling specific errorsasync function robustFetch() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return await response.json(); } catch (error) { if (error instanceof TypeError) { console.log('Network error:', error); } else { console.log('Other error:', error); } }}Interview Question: Execution Order
Section titled “Interview Question: Execution Order”console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve() .then(() => console.log('3')) .then(() => console.log('4'));
console.log('5');
// Output: 1, 5, 3, 4, 2// Why? Sync -> Microtask Queue (Promises) -> Macrotask Queue (setTimeout)