Back to Blog
JavaScriptPromisesAsyncMethods

JavaScript Promise Methods Guide

Master JavaScript Promise methods including all, race, any, allSettled, and more.

B
Bootspring Team
Engineering
November 28, 2018
6 min read

JavaScript Promises provide powerful methods for handling async operations. Here's a comprehensive guide to all Promise methods.

Promise.all()#

1// Wait for all promises to resolve 2const promise1 = fetch('/api/users'); 3const promise2 = fetch('/api/posts'); 4const promise3 = fetch('/api/comments'); 5 6const [users, posts, comments] = await Promise.all([ 7 promise1.then(r => r.json()), 8 promise2.then(r => r.json()), 9 promise3.then(r => r.json()), 10]); 11 12// Fails fast - rejects if ANY promise rejects 13try { 14 const results = await Promise.all([ 15 Promise.resolve(1), 16 Promise.reject(new Error('Failed')), 17 Promise.resolve(3), 18 ]); 19} catch (error) { 20 console.log(error.message); // 'Failed' 21} 22 23// Practical: Parallel API calls 24async function fetchUserData(userId) { 25 const [user, posts, followers] = await Promise.all([ 26 fetchUser(userId), 27 fetchUserPosts(userId), 28 fetchUserFollowers(userId), 29 ]); 30 31 return { user, posts, followers }; 32}

Promise.allSettled()#

1// Wait for all promises regardless of success/failure 2const results = await Promise.allSettled([ 3 Promise.resolve('Success'), 4 Promise.reject(new Error('Failed')), 5 Promise.resolve('Another success'), 6]); 7 8// Results array contains status of each 9results.forEach((result, index) => { 10 if (result.status === 'fulfilled') { 11 console.log(`Promise ${index}: ${result.value}`); 12 } else { 13 console.log(`Promise ${index}: ${result.reason.message}`); 14 } 15}); 16 17// Practical: Batch operations with error handling 18async function sendNotifications(users) { 19 const promises = users.map(user => 20 sendNotification(user.id, user.message) 21 ); 22 23 const results = await Promise.allSettled(promises); 24 25 const succeeded = results.filter(r => r.status === 'fulfilled').length; 26 const failed = results.filter(r => r.status === 'rejected').length; 27 28 console.log(`Sent: ${succeeded}, Failed: ${failed}`); 29 30 return results; 31}

Promise.race()#

1// First promise to settle wins 2const fastest = await Promise.race([ 3 fetch('/api/server1'), 4 fetch('/api/server2'), 5 fetch('/api/server3'), 6]); 7 8// Timeout pattern 9function withTimeout(promise, ms) { 10 const timeout = new Promise((_, reject) => 11 setTimeout(() => reject(new Error('Timeout')), ms) 12 ); 13 14 return Promise.race([promise, timeout]); 15} 16 17// Usage 18try { 19 const data = await withTimeout(fetch('/api/slow'), 5000); 20 console.log('Got data:', data); 21} catch (error) { 22 console.log('Request timed out'); 23} 24 25// First to reject also wins 26const result = await Promise.race([ 27 new Promise(resolve => setTimeout(() => resolve('slow'), 1000)), 28 Promise.reject(new Error('instant fail')), 29]); // Rejects immediately

Promise.any()#

1// First promise to FULFILL wins (ignores rejections) 2const first = await Promise.any([ 3 Promise.reject(new Error('Error 1')), 4 Promise.resolve('Success'), 5 Promise.reject(new Error('Error 2')), 6]); 7 8console.log(first); // 'Success' 9 10// Only rejects if ALL promises reject 11try { 12 await Promise.any([ 13 Promise.reject(new Error('Error 1')), 14 Promise.reject(new Error('Error 2')), 15 ]); 16} catch (error) { 17 console.log(error.name); // 'AggregateError' 18 console.log(error.errors); // [Error, Error] 19} 20 21// Practical: Try multiple sources 22async function fetchFromAnySource(urls) { 23 const promises = urls.map(url => 24 fetch(url).then(r => { 25 if (!r.ok) throw new Error(`HTTP ${r.status}`); 26 return r.json(); 27 }) 28 ); 29 30 return Promise.any(promises); 31}

Promise.resolve() & Promise.reject()#

1// Create resolved promise 2const resolved = Promise.resolve('value'); 3const resolvedObj = Promise.resolve({ data: 'test' }); 4 5// Resolve another promise (unwraps it) 6const nested = Promise.resolve(Promise.resolve('unwrapped')); 7console.log(await nested); // 'unwrapped' 8 9// Create rejected promise 10const rejected = Promise.reject(new Error('Failed')); 11 12// Practical: Normalize sync/async functions 13function maybeAsync(value) { 14 if (typeof value.then === 'function') { 15 return value; // Already a promise 16 } 17 return Promise.resolve(value); 18} 19 20// Cache with promises 21const cache = new Map(); 22 23function fetchWithCache(url) { 24 if (cache.has(url)) { 25 return Promise.resolve(cache.get(url)); 26 } 27 28 return fetch(url) 29 .then(r => r.json()) 30 .then(data => { 31 cache.set(url, data); 32 return data; 33 }); 34}

Promise.withResolvers()#

1// Create deferred promise (ES2024) 2const { promise, resolve, reject } = Promise.withResolvers(); 3 4// External control over resolution 5setTimeout(() => resolve('Done!'), 1000); 6 7const result = await promise; // 'Done!' 8 9// Practical: Event-based resolution 10function waitForEvent(element, event) { 11 const { promise, resolve } = Promise.withResolvers(); 12 13 element.addEventListener(event, resolve, { once: true }); 14 15 return promise; 16} 17 18const click = await waitForEvent(button, 'click');

Chaining Promises#

1// Basic chain 2fetch('/api/user') 3 .then(response => response.json()) 4 .then(user => fetch(`/api/posts?userId=${user.id}`)) 5 .then(response => response.json()) 6 .then(posts => console.log(posts)) 7 .catch(error => console.error(error)); 8 9// Return values pass through chain 10Promise.resolve(1) 11 .then(x => x + 1) // 2 12 .then(x => x * 2) // 4 13 .then(x => x + 3) // 7 14 .then(console.log); // 7 15 16// Throwing in then creates rejection 17Promise.resolve('test') 18 .then(value => { 19 throw new Error('Oops'); 20 }) 21 .catch(error => { 22 console.log(error.message); // 'Oops' 23 });

Error Handling#

1// catch() handles rejections 2fetch('/api/data') 3 .then(r => r.json()) 4 .catch(error => { 5 console.error('Fetch failed:', error); 6 return { fallback: true }; 7 }) 8 .then(data => console.log(data)); 9 10// finally() runs regardless 11fetch('/api/data') 12 .then(r => r.json()) 13 .catch(error => console.error(error)) 14 .finally(() => { 15 console.log('Cleanup'); 16 hideLoadingSpinner(); 17 }); 18 19// Multiple catch blocks 20fetch('/api/data') 21 .then(r => { 22 if (!r.ok) throw new Error(`HTTP ${r.status}`); 23 return r.json(); 24 }) 25 .catch(error => { 26 // Handle HTTP errors 27 if (error.message.startsWith('HTTP')) { 28 return fetchFallback(); 29 } 30 throw error; // Re-throw others 31 }) 32 .catch(error => { 33 // Handle all other errors 34 console.error('Unhandled:', error); 35 });

Concurrent Patterns#

1// Limit concurrency 2async function mapLimit(items, limit, fn) { 3 const results = []; 4 const executing = new Set(); 5 6 for (const [index, item] of items.entries()) { 7 const promise = fn(item, index).then(result => { 8 executing.delete(promise); 9 return result; 10 }); 11 12 results.push(promise); 13 executing.add(promise); 14 15 if (executing.size >= limit) { 16 await Promise.race(executing); 17 } 18 } 19 20 return Promise.all(results); 21} 22 23// Usage 24await mapLimit(urls, 3, async (url) => { 25 const response = await fetch(url); 26 return response.json(); 27}); 28 29// Sequential execution 30async function sequential(items, fn) { 31 const results = []; 32 for (const item of items) { 33 results.push(await fn(item)); 34 } 35 return results; 36}

Creating Promises#

1// Basic promise constructor 2const promise = new Promise((resolve, reject) => { 3 // Async operation 4 setTimeout(() => { 5 const success = Math.random() > 0.5; 6 if (success) { 7 resolve('Success!'); 8 } else { 9 reject(new Error('Failed!')); 10 } 11 }, 1000); 12}); 13 14// Promisify callback-based function 15function promisify(fn) { 16 return (...args) => { 17 return new Promise((resolve, reject) => { 18 fn(...args, (error, result) => { 19 if (error) reject(error); 20 else resolve(result); 21 }); 22 }); 23 }; 24} 25 26// Usage 27const readFile = promisify(fs.readFile); 28const content = await readFile('file.txt', 'utf8');

Practical Patterns#

1// Retry with exponential backoff 2async function retry(fn, maxAttempts = 3, delay = 1000) { 3 for (let attempt = 1; attempt <= maxAttempts; attempt++) { 4 try { 5 return await fn(); 6 } catch (error) { 7 if (attempt === maxAttempts) throw error; 8 await new Promise(r => setTimeout(r, delay * attempt)); 9 } 10 } 11} 12 13// Debounce async 14function debounceAsync(fn, wait) { 15 let timeout; 16 let pending; 17 18 return (...args) => { 19 clearTimeout(timeout); 20 21 return new Promise((resolve, reject) => { 22 timeout = setTimeout(async () => { 23 try { 24 const result = await fn(...args); 25 resolve(result); 26 } catch (error) { 27 reject(error); 28 } 29 }, wait); 30 }); 31 }; 32}

Best Practices#

Error Handling: ✓ Always add catch handlers ✓ Use finally for cleanup ✓ Re-throw when appropriate ✓ Log unexpected errors Patterns: ✓ Use Promise.all for parallel ✓ Use Promise.allSettled for batch ✓ Use Promise.race for timeout ✓ Use Promise.any for redundancy Performance: ✓ Start promises early ✓ Limit concurrency ✓ Avoid unnecessary awaits ✓ Use parallel when possible Avoid: ✗ Unhandled rejections ✗ Nesting promises unnecessarily ✗ Mixing callbacks and promises ✗ Forgetting to return promises

Conclusion#

Promise methods provide powerful tools for managing async operations. Use Promise.all() for parallel execution, Promise.allSettled() when you need all results regardless of failures, Promise.race() for timeouts, and Promise.any() for redundant sources. Always handle errors appropriately and consider concurrency limits for batch operations.

Share this article

Help spread the word about Bootspring