Back to Blog
JavaScriptPromisesAsyncMethods

JavaScript Promise Methods Guide

Master JavaScript Promise methods including all, race, allSettled, and any for async operations.

B
Bootspring Team
Engineering
March 10, 2020
7 min read

JavaScript provides several Promise combinators for handling multiple async operations. Here's how to use them.

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 6Promise.all([promise1, promise2, promise3]) 7 .then(([users, posts, comments]) => { 8 console.log('All data loaded'); 9 }) 10 .catch(error => { 11 // If ANY promise rejects, catch fires 12 console.error('One request failed:', error); 13 }); 14 15// With async/await 16async function loadAllData() { 17 try { 18 const [users, posts, comments] = await Promise.all([ 19 fetch('/api/users').then(r => r.json()), 20 fetch('/api/posts').then(r => r.json()), 21 fetch('/api/comments').then(r => r.json()), 22 ]); 23 24 return { users, posts, comments }; 25 } catch (error) { 26 console.error('Failed to load data:', error); 27 throw error; 28 } 29} 30 31// Parallel processing 32async function processItems(items) { 33 const results = await Promise.all( 34 items.map(async item => { 35 const processed = await processItem(item); 36 return processed; 37 }) 38 ); 39 40 return results; 41} 42 43// With timeout 44function withTimeout(promise, ms) { 45 const timeout = new Promise((_, reject) => 46 setTimeout(() => reject(new Error('Timeout')), ms) 47 ); 48 return Promise.race([promise, timeout]); 49} 50 51const results = await withTimeout( 52 Promise.all([fetch('/api/a'), fetch('/api/b')]), 53 5000 54);

Promise.allSettled#

1// Wait for all promises, regardless of outcome 2const promises = [ 3 fetch('/api/users'), 4 fetch('/api/broken-endpoint'), // This will fail 5 fetch('/api/posts'), 6]; 7 8Promise.allSettled(promises) 9 .then(results => { 10 results.forEach((result, index) => { 11 if (result.status === 'fulfilled') { 12 console.log(`Promise ${index} succeeded:`, result.value); 13 } else { 14 console.log(`Promise ${index} failed:`, result.reason); 15 } 16 }); 17 }); 18 19// Separate successes and failures 20async function fetchAllWithStatus(urls) { 21 const results = await Promise.allSettled( 22 urls.map(url => fetch(url).then(r => r.json())) 23 ); 24 25 const successes = results 26 .filter(r => r.status === 'fulfilled') 27 .map(r => r.value); 28 29 const failures = results 30 .filter(r => r.status === 'rejected') 31 .map(r => r.reason); 32 33 return { successes, failures }; 34} 35 36// Retry failed requests 37async function fetchWithRetry(urls, maxRetries = 3) { 38 let remaining = urls; 39 40 for (let attempt = 0; attempt < maxRetries; attempt++) { 41 const results = await Promise.allSettled( 42 remaining.map(url => fetch(url).then(r => r.json())) 43 ); 44 45 const succeeded = []; 46 const failed = []; 47 48 results.forEach((result, index) => { 49 if (result.status === 'fulfilled') { 50 succeeded.push(result.value); 51 } else { 52 failed.push(remaining[index]); 53 } 54 }); 55 56 if (failed.length === 0) { 57 return succeeded; 58 } 59 60 remaining = failed; 61 } 62 63 throw new Error(`Failed after ${maxRetries} retries`); 64}

Promise.race#

1// First promise to settle wins 2const fast = new Promise(resolve => 3 setTimeout(() => resolve('fast'), 100) 4); 5 6const slow = new Promise(resolve => 7 setTimeout(() => resolve('slow'), 500) 8); 9 10Promise.race([fast, slow]) 11 .then(result => console.log(result)); // 'fast' 12 13// Timeout pattern 14function fetchWithTimeout(url, timeout) { 15 const fetchPromise = fetch(url); 16 const timeoutPromise = new Promise((_, reject) => 17 setTimeout(() => reject(new Error('Request timeout')), timeout) 18 ); 19 20 return Promise.race([fetchPromise, timeoutPromise]); 21} 22 23try { 24 const response = await fetchWithTimeout('/api/data', 5000); 25 const data = await response.json(); 26} catch (error) { 27 if (error.message === 'Request timeout') { 28 console.log('Request took too long'); 29 } 30} 31 32// First successful response from multiple sources 33async function fetchFromMirrors(mirrors) { 34 return Promise.race( 35 mirrors.map(url => 36 fetch(url).then(response => { 37 if (!response.ok) throw new Error('Failed'); 38 return response; 39 }) 40 ) 41 ); 42} 43 44// Cancel on first result 45function raceWithCancellation(promises, signal) { 46 return Promise.race([ 47 ...promises, 48 new Promise((_, reject) => { 49 signal.addEventListener('abort', () => { 50 reject(new DOMException('Aborted', 'AbortError')); 51 }); 52 }), 53 ]); 54}

Promise.any#

1// First promise to FULFILL wins (ignores rejections) 2const promises = [ 3 Promise.reject('Error 1'), 4 new Promise(resolve => setTimeout(() => resolve('Success'), 100)), 5 Promise.reject('Error 2'), 6]; 7 8Promise.any(promises) 9 .then(result => console.log(result)) // 'Success' 10 .catch(error => { 11 // AggregateError if ALL promises reject 12 console.log(error.errors); // All rejection reasons 13 }); 14 15// Fallback pattern 16async function fetchWithFallbacks(urls) { 17 try { 18 const response = await Promise.any( 19 urls.map(url => fetch(url).then(r => { 20 if (!r.ok) throw new Error(`HTTP ${r.status}`); 21 return r; 22 })) 23 ); 24 return response.json(); 25 } catch (error) { 26 if (error instanceof AggregateError) { 27 console.log('All sources failed:', error.errors); 28 } 29 throw error; 30 } 31} 32 33// First available service 34async function connectToService(endpoints) { 35 try { 36 const connection = await Promise.any( 37 endpoints.map(async endpoint => { 38 const health = await fetch(`${endpoint}/health`); 39 if (!health.ok) throw new Error('Unhealthy'); 40 return { endpoint, status: 'connected' }; 41 }) 42 ); 43 return connection; 44 } catch (error) { 45 throw new Error('No healthy services available'); 46 } 47}

Promise.resolve and Promise.reject#

1// Create resolved promise 2const resolved = Promise.resolve(42); 3resolved.then(value => console.log(value)); // 42 4 5// Wrap sync value in promise 6function maybeAsync(value) { 7 return Promise.resolve(value); 8} 9 10// Create rejected promise 11const rejected = Promise.reject(new Error('Failed')); 12rejected.catch(error => console.log(error.message)); // 'Failed' 13 14// Normalize to promise 15async function getData(source) { 16 // Handle both sync and async sources 17 const data = await Promise.resolve(source); 18 return process(data); 19} 20 21// Thenable conversion 22const thenable = { 23 then(resolve) { 24 resolve('from thenable'); 25 } 26}; 27 28Promise.resolve(thenable) 29 .then(value => console.log(value)); // 'from thenable'

Practical Patterns#

1// Batch processing with concurrency limit 2async function batchProcess(items, fn, concurrency = 5) { 3 const results = []; 4 5 for (let i = 0; i < items.length; i += concurrency) { 6 const batch = items.slice(i, i + concurrency); 7 const batchResults = await Promise.all(batch.map(fn)); 8 results.push(...batchResults); 9 } 10 11 return results; 12} 13 14// Sequential execution 15async function sequential(tasks) { 16 const results = []; 17 18 for (const task of tasks) { 19 results.push(await task()); 20 } 21 22 return results; 23} 24 25// Or with reduce 26function sequentialReduce(tasks) { 27 return tasks.reduce( 28 (promise, task) => promise.then(results => 29 task().then(result => [...results, result]) 30 ), 31 Promise.resolve([]) 32 ); 33} 34 35// Retry with exponential backoff 36async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) { 37 let lastError; 38 39 for (let i = 0; i < maxRetries; i++) { 40 try { 41 return await fn(); 42 } catch (error) { 43 lastError = error; 44 const delay = baseDelay * Math.pow(2, i); 45 await new Promise(r => setTimeout(r, delay)); 46 } 47 } 48 49 throw lastError; 50} 51 52// Poll until condition 53async function pollUntil(fn, condition, interval = 1000, timeout = 30000) { 54 const start = Date.now(); 55 56 while (Date.now() - start < timeout) { 57 const result = await fn(); 58 if (condition(result)) { 59 return result; 60 } 61 await new Promise(r => setTimeout(r, interval)); 62 } 63 64 throw new Error('Polling timeout'); 65} 66 67// Usage 68const status = await pollUntil( 69 () => fetch('/api/job/123').then(r => r.json()), 70 result => result.status === 'complete', 71 2000, 72 60000 73);

Error Handling#

1// Catch specific errors 2class NetworkError extends Error {} 3class ValidationError extends Error {} 4 5async function fetchData(url) { 6 try { 7 const response = await fetch(url); 8 9 if (!response.ok) { 10 throw new NetworkError(`HTTP ${response.status}`); 11 } 12 13 const data = await response.json(); 14 15 if (!isValid(data)) { 16 throw new ValidationError('Invalid data'); 17 } 18 19 return data; 20 } catch (error) { 21 if (error instanceof NetworkError) { 22 // Handle network errors 23 } else if (error instanceof ValidationError) { 24 // Handle validation errors 25 } else { 26 // Unknown error 27 throw error; 28 } 29 } 30} 31 32// Finally for cleanup 33async function withCleanup() { 34 const resource = await acquire(); 35 36 try { 37 return await useResource(resource); 38 } finally { 39 await release(resource); 40 } 41} 42 43// Promise.prototype.finally 44fetch('/api/data') 45 .then(response => response.json()) 46 .catch(error => console.error(error)) 47 .finally(() => { 48 hideLoadingSpinner(); 49 });

Best Practices#

Choosing Methods: ✓ Promise.all - all must succeed ✓ Promise.allSettled - need all results ✓ Promise.race - first to settle ✓ Promise.any - first to succeed Error Handling: ✓ Always handle rejections ✓ Use specific error types ✓ Add timeouts to prevent hanging ✓ Log errors appropriately Performance: ✓ Parallelize independent operations ✓ Limit concurrency for resources ✓ Batch large operations ✓ Cancel unnecessary requests Avoid: ✗ Unhandled promise rejections ✗ Infinite parallel requests ✗ Forgetting await ✗ Nested promise chains

Conclusion#

Promise combinators enable powerful async patterns. Use Promise.all when all operations must succeed, Promise.allSettled when you need all results regardless of outcome, Promise.race for timeouts and first-response patterns, and Promise.any for fallback strategies. Combine these with proper error handling for robust async code.

Share this article

Help spread the word about Bootspring