JavaScript Promises: From Basics to Advanced Patterns
Master JavaScript Promises for handling asynchronous operations. Learn promise chaining, error handling, Promise.all, Promise.race, and modern async patterns.
Promises are the foundation of asynchronous JavaScript. They represent a value that may not be available yet but will be resolved in the future.
What is a Promise?
A Promise is an object representing the eventual completion or failure of an async operation. It can be in one of three states: pending, fulfilled, or rejected.
```javascript // Creating a simple promise const myPromise = new Promise((resolve, reject) => { const success = true; setTimeout(() => { if (success) { resolve('Operation successful!'); } else { reject('Operation failed!'); } }, 1000); });
// Using the promise myPromise .then(result => console.log(result)) .catch(error => console.error(error)); ```
Promise Chaining
Chain multiple async operations together. Each `.then()` returns a new promise.
```javascript fetch('https://api.example.com/user/1') .then(response => response.json()) .then(user => { console.log('User:', user.name); return fetch(`https://api.example.com/posts/${user.id}`); }) .then(response => response.json()) .then(posts => { console.log('Posts:', posts); }) .catch(error => { console.error('Error:', error); }); ```
Promise.all - Wait for Multiple Promises
Run multiple promises in parallel and wait for all to complete.
```javascript const promise1 = fetch('https://api.example.com/users'); const promise2 = fetch('https://api.example.com/posts'); const promise3 = fetch('https://api.example.com/comments');
Promise.all([promise1, promise2, promise3]) .then(responses => { // All promises resolved return Promise.all(responses.map(r => r.json())); }) .then(([users, posts, comments]) => { console.log('Users:', users); console.log('Posts:', posts); console.log('Comments:', comments); }) .catch(error => { // If any promise rejects console.error('One of the requests failed:', error); }); ```
Promise.race - First to Finish Wins
Use `Promise.race()` when you want the result of whichever promise completes first.
```javascript const slowPromise = new Promise(resolve => setTimeout(() => resolve('Slow'), 3000) );
const fastPromise = new Promise(resolve => setTimeout(() => resolve('Fast'), 1000) );
Promise.race([slowPromise, fastPromise]) .then(result => console.log(result)); // 'Fast'
// Timeout pattern const fetchWithTimeout = (url, timeout = 5000) => { return Promise.race([ fetch(url), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ) ]); }; ```
Error Handling
Handle errors properly in promise chains. Errors bubble up to the nearest `.catch()`.
```javascript fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error('Failed to fetch:', error.message); }) .finally(() => { console.log('Request completed'); }); ```
Async/Await - Modern Promise Syntax
Async/await makes promises easier to work with. It's syntactic sugar over promises.
```javascript // Traditional promise chain function getUser(id) { return fetch(`https://api.example.com/user/${id}`) .then(response => response.json()) .then(user => user); }
// With async/await async function getUser(id) { const response = await fetch(`https://api.example.com/user/${id}`); const user = await response.json(); return user; }
// Error handling with async/await async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); throw error; } } ```
Real-World Example
Complete example combining everything: fetching user data, their posts, and handling errors.
```javascript async function getUserDashboard(userId) { try { // Fetch user and posts in parallel const [userResponse, postsResponse] = await Promise.all([ fetch(`https://api.example.com/users/${userId}`), fetch(`https://api.example.com/posts?userId=${userId}`) ]);
// Check if responses are ok if (!userResponse.ok || !postsResponse.ok) { throw new Error('Failed to fetch data'); }
// Parse JSON in parallel const [user, posts] = await Promise.all([ userResponse.json(), postsResponse.json() ]);
// Return combined data return { user, posts, postCount: posts.length }; } catch (error) { console.error('Dashboard error:', error); return null; } }
// Usage getUserDashboard(123).then(dashboard => { if (dashboard) { console.log(`${dashboard.user.name} has ${dashboard.postCount} posts`); } }); ```