Promises Deep Dive
MedPromises are JavaScript's primary abstraction for asynchronous operations. Understanding Promise internals, combinators (all/race/any/allSettled), and error handling patterns is essential for writing robust async code.
Interactive Visualization
1const p = new Promise((resolve, reject) => {2 setTimeout(() => {3 resolve("Success!");4 }, 1000);5});67p.then(value => {8 console.log(value);9});
Key Points
- A Promise represents a value that may be available now, later, or never
- Promise states: pending → fulfilled OR rejected (immutable once settled)
- Promise.all() fails fast: rejects on first rejection
- Promise.race() returns first settled (fulfilled OR rejected)
- Promise.any() returns first fulfilled (ignores rejections until all fail)
- Promise.allSettled() waits for all, never short-circuits
- .then() always returns a new Promise (enabling chaining)
- Unhandled rejections are dangerous - always add .catch() or try/catch
Code Examples
Promise States
// Creating Promises const pending = new Promise(() => {}); // stays pending const fulfilled = Promise.resolve(42); // immediately fulfilled const rejected = Promise.reject('err'); // immediately rejected // State transitions are ONE-WAY and IMMUTABLE const p = new Promise((resolve, reject) => { resolve('first'); // Promise is now fulfilled resolve('second'); // ignored! reject('error'); // ignored! }); p.then(v => console.log(v)); // "first"
Once a Promise settles, its state and value are locked forever
Promise.all() - Fail Fast
// All must succeed const results = await Promise.all([ fetch('/api/users'), fetch('/api/posts'), fetch('/api/comments') ]); // results = [usersResponse, postsResponse, commentsResponse] // One failure = immediate rejection await Promise.all([ Promise.resolve(1), Promise.reject('Error!'), // Fails here Promise.resolve(3) // Never awaited! ]); // Throws: "Error!"
Use all() when you need ALL results and want fast failure
Promise.race() - First Wins
// Timeout pattern async function fetchWithTimeout(url, ms) { const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms) ); return Promise.race([fetch(url), timeout]); } // First to settle wins (success OR failure) await Promise.race([ fetch('/slow'), // takes 5s fetch('/fast') // takes 1s - this wins! ]);
Use race() for timeouts or "first response wins" patterns
Promise.any() - First Success
// Try multiple sources, take first success const data = await Promise.any([ fetch('https://primary.api/data'), fetch('https://backup.api/data'), fetch('https://fallback.api/data') ]); // Returns first successful response // Only fails if ALL fail await Promise.any([ Promise.reject('A failed'), Promise.reject('B failed'), Promise.reject('C failed') ]); // Throws: AggregateError with all rejection reasons
Use any() for redundant sources or fallback chains
Promise.allSettled() - Wait for All
// Get results regardless of success/failure const results = await Promise.allSettled([ Promise.resolve('success'), Promise.reject('error'), Promise.resolve('another') ]); // results = [ // { status: 'fulfilled', value: 'success' }, // { status: 'rejected', reason: 'error' }, // { status: 'fulfilled', value: 'another' } // ] // Filter successes const successes = results .filter(r => r.status === 'fulfilled') .map(r => r.value);
Use allSettled() when you need all results regardless of failures
Promisify Callback APIs
// Convert callback-based function to Promise function promisify(fn) { return function(...args) { return new Promise((resolve, reject) => { fn(...args, (err, result) => { if (err) reject(err); else resolve(result); }); }); }; } // Usage const readFile = promisify(fs.readFile); const data = await readFile('file.txt', 'utf8');
Promisify wraps Node-style callbacks (err, result) into Promises
Retry with Exponential Backoff
async function retry(fn, retries = 3, delay = 1000) { for (let i = 0; i < retries; i++) { try { return await fn(); } catch (err) { if (i === retries - 1) throw err; await new Promise(r => setTimeout(r, delay * Math.pow(2, i))); } } } // Usage const data = await retry( () => fetch('/flaky-api'), 3, // 3 attempts 1000 // 1s, 2s, 4s delays );
Retry pattern with increasing delays between attempts
Common Mistakes
- Forgetting to return in .then() chains (breaks the chain)
- Not handling Promise rejections (.catch or try/catch)
- Using Promise.all() when you need allSettled() behavior
- Creating promises in loops without proper batching
- Mixing async/await with .then() inconsistently
Interview Tips
- Know the difference between all/race/any/allSettled
- Implement Promise.all() from scratch
- Explain the microtask queue and Promise execution order
- Write a promisify function
- Implement retry with exponential backoff
Practice Problems
Apply this concept by solving these 12 problems
Implement Promise.all
MedParallel execution
Implement Promise.race
EasyFirst to settle wins
Implement Promisify
MedConvert callbacks to promises
Implement Promise.allSettled
MedWait for all promises regardless of outcome
Implement Promise.any
MedFirst fulfilled promise wins
Implement Promise.finally
EasyAlways runs after promise settled
Auto-retry Promise
MedRetry failed promises automatically
Promise with Timeout
EasyReject if promise takes too long
Throttle Promises
HardLimit concurrent promise execution
Sequence Async Tasks
MedRun promises sequentially
Create Your Own Promise
HardImplement Promise from scratch
Promise Chaining
EasySequential async with promises