JavaScript 42 🧬 Promises
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise.then(value => {
console.log('Success:', value);
});
const failing = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing.catch(err => {
console.log('Error:', err.message);
});
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
Promise.all([p1, p2, p3]).then(results => {
console.log(results);
});
Promise.allSettled([p1, p2, p3]).then(results => {
results.forEach(r => console.log(r.status));
});
Promise.race([p1, p2]).then(first => {
console.log('First:', first);
});
Promise.any([p1, p2]).then(firstSuccess => {
console.log('First success:', firstSuccess);
});
A Promise is JavaScript’s way of representing a value that isn’t available yet but will be — or won’t be — at some point in the future. It’s the foundation of modern asynchronous code, and it solves the “callback hell” problem that plagued JavaScript before ES6.
Key point: A Promise is an object with three possible states: pending, fulfilled, or rejected. Once settled (fulfilled or rejected), a Promise is immutable — it can never change state again. You attach callbacks with .then(), .catch(), and .finally() to react to the outcome.
a – What is a Promise
A Promise represents the eventual result of an asynchronous operation. It’s a placeholder for a value that may not exist yet.
The three states:
| State | Meaning |
|---|---|
| Pending | The operation hasn’t finished |
| Fulfilled | The operation succeeded — has a value |
| Rejected | The operation failed — has a reason (usually an Error) |
Once a Promise is settled (fulfilled or rejected), it stays that way forever.
State transitions:
┌──────────────────────────────────────────────┐
│ Promise states │
│ │
│ ┌──────────┐ │
│ │ pending │ │
│ └────┬─────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────┐ ┌────────────┐ │
│ │ fulfilled │ │ rejected │ │
│ └───────────┘ └────────────┘ │
│ │
│ settled — never changes again │
│ │
└──────────────────────────────────────────────┘
Creating a Promise:
const promise = new Promise((resolve, reject) => {
// async work here
if (success) {
resolve(value);
} else {
reject(new Error('reason'));
}
});
The executor function runs immediately and synchronously. Inside it, you call resolve() or reject() when the async work completes.
A simple example:
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise.then(value => {
console.log('Success:', value);
});
// (after 1 second)
// [ Success: done ]
A failing Promise:
const failing = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing.catch(err => {
console.log('Error:', err.message);
});
// (after 1 second)
// [ Error: failed ]
Why Promises matter:
- No callback hell — chain with
.then()instead of nesting - Error handling —
.catch()catches any failure in the chain - Composability —
Promise.all,Promise.race, etc. - Cleaner async — the foundation for
async/await - Standard — built into the language, no library needed
Promise vs callback:
// ❌ Callback style — nested, hard to read
getUser(id, (user) => {
getOrders(user.id, (orders) => {
getDetails(orders[0].id, (details) => {
console.log(details);
});
});
});
// ✅ Promise style — flat, chainable
getUser(id)
.then(user => getOrders(user.id))
.then(orders => getDetails(orders[0].id))
.then(details => console.log(details))
.catch(err => console.error(err));
A Promise is not a value — it’s a container:
const p = Promise.resolve(42);
console.log(p);
// [ Promise { 42 } ]
// To get the value, use .then() or await
p.then(v => console.log(v));
// [ 42 ]
Logging a Promise shows its state and value — but you can’t read the value directly.
b – Promise methods and chaining
Promises have three instance methods for reacting to their outcome, plus several static methods for combining Promises.
Instance methods:
| Method | Purpose |
|---|---|
.then(onFulfilled, onRejected) | Handle success (and optionally failure) |
.catch(onRejected) | Handle failure only |
.finally(onFinally) | Run cleanup regardless of outcome |
.then() — handle success:
Promise.resolve(42).then(value => {
console.log(value);
});
// [ 42 ]
Chaining .then():
Each .then() returns a new Promise, which lets you chain:
Promise.resolve(1)
.then(v => v + 1)
.then(v => v * 2)
.then(v => console.log(v));
// [ 4 ]
The return value of one .then() becomes the input to the next.
Returning a Promise from .then():
fetch('/user')
.then(res => res.json()) // returns a Promise
.then(user => fetch(`/orders/${user.id}`))
.then(res => res.json())
.then(orders => console.log(orders));
The chain waits for each Promise to resolve.
.catch() — handle failure:
Promise.reject(new Error('oops'))
.catch(err => {
console.log('Caught:', err.message);
});
// [ Caught: oops ]
.catch() is shorthand for .then(null, onRejected).
.catch() catches errors from anywhere in the chain:
Promise.resolve(1)
.then(v => v + 1)
.then(() => { throw new Error('mid-chain'); })
.then(v => console.log(v)) // skipped
.catch(err => console.log('Caught:', err.message));
// [ Caught: mid-chain ]
Any error thrown in .then() skips to the next .catch().
.finally() — always runs:
showSpinner();
fetch('/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => hideSpinner());
.finally() doesn’t receive the value or the error — it just runs cleanup.
Chaining pattern — the full flow:
Promise.resolve(1)
.then(v => {
console.log('Step 1:', v);
return v + 1;
})
.then(v => {
console.log('Step 2:', v);
return v + 1;
})
.catch(err => {
console.log('Error:', err.message);
})
.finally(() => {
console.log('Done');
});
// [ Step 1: 1 ]
// [ Step 2: 2 ]
// [ Done ]
Return values propagate:
Promise.resolve(2)
.then(v => v * 10)
.then(v => v + 1)
.then(v => console.log(v));
// [ 21 ]
Async functions return Promises automatically:
async function getData() {
return 42;
}
getData().then(v => console.log(v));
// [ 42 ]
Static methods for combining Promises:
| Method | Purpose |
|---|---|
Promise.all(iterable) | Wait for all — reject if any fails |
Promise.allSettled(iterable) | Wait for all, never rejects |
Promise.race(iterable) | Settle with the first to finish |
Promise.any(iterable) | Resolve with the first success |
Promise.resolve(value) | Wrap a value in a resolved Promise |
Promise.reject(reason) | Create a rejected Promise |
Promise.all — wait for all, fail fast:
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(values => {
console.log(values);
});
// [ [ 1, 2, 3 ] ]
If any rejects, the whole thing rejects:
Promise.all([
Promise.resolve(1),
Promise.reject(new Error('fail')),
Promise.resolve(3)
]).catch(err => console.log(err.message));
// [ fail ]
Promise.allSettled — wait for all, never rejects:
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('fail')),
Promise.resolve(3)
]).then(results => {
console.log(results);
});
// [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: Error: fail },
// { status: 'fulfilled', value: 3 }
// ]
Promise.race — first to settle wins:
Promise.race([
new Promise(r => setTimeout(() => r('slow'), 1000)),
new Promise(r => setTimeout(() => r('fast'), 100))
]).then(winner => console.log(winner));
// [ fast ]
The first Promise to settle (fulfill or reject) wins.
Promise.any — first success wins:
Promise.any([
Promise.reject(new Error('a')),
Promise.resolve('b'),
Promise.resolve('c')
]).then(first => console.log(first));
// [ b ]
It ignores rejections unless all reject.
Comparison table:
| Method | Waits for | Rejects when |
|---|---|---|
all | All | Any rejects |
allSettled | All | Never |
race | First to settle | First rejects |
any | First to fulfill | All reject |
Promise.resolve and Promise.reject:
Promise.resolve(42).then(v => console.log(v));
// [ 42 ]
Promise.reject(new Error('x')).catch(e => console.log(e.message));
// [ x ]
Useful for testing and for normalizing values into Promises.
A practical example — fetch multiple resources:
Promise.all([
fetch('/user').then(r => r.json()),
fetch('/orders').then(r => r.json()),
fetch('/products').then(r => r.json())
]).then(([user, orders, products]) => {
console.log('User:', user);
console.log('Orders:', orders);
console.log('Products:', products);
});
All three fetches run in parallel. The .then() receives an array of results.
Error handling in chains:
fetch('/data')
.then(res => {
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
})
.then(data => console.log(data))
.catch(err => console.error('Failed:', err.message))
.finally(() => console.log('Done'));
c – Common Promise patterns
Promises enable several recurring patterns that come up constantly in real code.
Pattern 1 — Sequential execution:
async function sequential() {
const a = await step1();
const b = await step2(a);
const c = await step3(b);
return c;
}
Each step waits for the previous one.
Pattern 2 — Parallel execution:
async function parallel() {
const [a, b, c] = await Promise.all([
step1(),
step2(),
step3()
]);
return [a, b, c];
}
All three run at the same time.
Pattern 3 — Racing with timeout:
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
withTimeout(fetch('/slow'), 5000)
.then(res => console.log(res))
.catch(err => console.log(err.message));
Pattern 4 — Retry on failure:
async function retry(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
console.log(`Attempt ${i + 1} failed, retrying...`);
}
}
}
retry(() => fetch('/flaky')).catch(err => console.log(err));
Pattern 5 — Fallback to default:
function withFallback(promise, fallback) {
return promise.catch(() => fallback);
}
withFallback(fetch('/data').then(r => r.json()), {})
.then(data => console.log(data));
Pattern 6 — Promisifying callbacks:
function promisify(fn) {
return (...args) =>
new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
const readFile = promisify(require('fs').readFile);
readFile('/etc/passwd', 'utf8')
.then(content => console.log(content))
.catch(err => console.log(err));
Pattern 7 — Loading all, ignoring failures:
Promise.allSettled(urls.map(u => fetch(u)))
.then(results => {
const successes = results.filter(r => r.status === 'fulfilled');
const failures = results.filter(r => r.status === 'rejected');
console.log(`${successes.length} succeeded, ${failures.length} failed`);
});
Pattern 8 — Racing multiple sources:
Promise.any([
fetch('/api/primary').then(r => r.json()),
fetch('/api/backup').then(r => r.json())
]).then(data => console.log('Got from fastest:', data));
Pattern 9 — Limiting concurrency:
async function mapLimit(items, limit, fn) {
const results = [];
for (let i = 0; i < items.length; i += limit) {
const chunk = items.slice(i, i + limit);
results.push(...await Promise.all(chunk.map(fn)));
}
return results;
}
Pattern 10 — Cancelling via race:
let cancelled = false;
const promise = fetch('/data');
Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => {
cancelled = true;
reject(new Error('Cancelled'));
}, 1000)
)
]).catch(err => console.log(err.message));
Pattern 11 — Await multiple without blocking:
const p1 = fetch('/a');
const p2 = fetch('/b');
const p3 = fetch('/c');
const [a, b, c] = await Promise.all([p1, p2, p3]);
Start them all first, then await together.
Pattern 12 — Converting callback APIs:
const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);
const data = await readFile('/etc/passwd', 'utf8');
Node.js has a built-in promisify.
A complete example — parallel with error handling:
async function loadDashboard(userId) {
const results = await Promise.allSettled([
fetch(`/user/${userId}`).then(r => r.json()),
fetch(`/user/${userId}/orders`).then(r => r.json()),
fetch(`/user/${userId}/messages`).then(r => r.json())
]);
const [user, orders, messages] = results.map(r =>
r.status === 'fulfilled' ? r.value : null
);
return { user, orders, messages };
}
Common patterns summary:
| Pattern | Use |
|---|---|
| Sequential | Each step depends on the previous |
| Parallel | Independent tasks |
| Timeout | Race against a timer |
| Retry | Re-attempt on failure |
| Fallback | Provide default on error |
| Promisify | Wrap callback APIs |
| allSettled | Handle partial failures |
| any | Fastest success wins |
| race | First result (success or fail) |
Complete Example Session
// ============================================
// PART 1: CREATING A PROMISE
// ============================================
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise.then(value => {
console.log('Success:', value);
});
// (after 1s)
// [ Success: done ]
// ============================================
// PART 2: REJECTED PROMISE
// ============================================
const failing = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing.catch(err => {
console.log('Error:', err.message);
});
// (after 1s)
// [ Error: failed ]
// ============================================
// PART 3: CHAINING .THEN
// ============================================
Promise.resolve(1)
.then(v => v + 1)
.then(v => v * 2)
.then(v => console.log(v));
// [ 4 ]
// ============================================
// PART 4: RETURNING PROMISES
// ============================================
Promise.resolve(1)
.then(v => Promise.resolve(v + 1))
.then(v => console.log(v));
// [ 2 ]
// ============================================
// PART 5: THROW IN CHAIN
// ============================================
Promise.resolve(1)
.then(v => { throw new Error('mid-chain'); })
.then(v => console.log(v))
.catch(err => console.log('Caught:', err.message));
// [ Caught: mid-chain ]
// ============================================
// PART 6: FINALLY
// ============================================
Promise.resolve('ok')
.then(v => console.log(v))
.catch(err => console.log(err))
.finally(() => console.log('Done'));
// [ ok ]
// [ Done ]
// ============================================
// PART 7: PROMISE.ALL
// ============================================
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(values => console.log(values));
// [ [ 1, 2, 3 ] ]
// ============================================
// PART 8: PROMISE.ALL WITH FAILURE
// ============================================
Promise.all([
Promise.resolve(1),
Promise.reject(new Error('fail')),
Promise.resolve(3)
]).catch(err => console.log(err.message));
// [ fail ]
// ============================================
// PART 9: PROMISE.ALLSETTLED
// ============================================
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('fail')),
Promise.resolve(3)
]).then(results => {
console.log(results.map(r => r.status));
});
// [ [ 'fulfilled', 'rejected', 'fulfilled' ] ]
// ============================================
// PART 10: PROMISE.RACE
// ============================================
Promise.race([
new Promise(r => setTimeout(() => r('slow'), 1000)),
new Promise(r => setTimeout(() => r('fast'), 100))
]).then(winner => console.log(winner));
// [ fast ]
// ============================================
// PART 11: PROMISE.ANY
// ============================================
Promise.any([
Promise.reject(new Error('a')),
Promise.resolve('b'),
Promise.resolve('c')
]).then(first => console.log(first));
// [ b ]
// ============================================
// PART 12: PROMISE.RESOLVE / REJECT
// ============================================
Promise.resolve(42).then(v => console.log(v));
// [ 42 ]
Promise.reject(new Error('x')).catch(e => console.log(e.message));
// [ x ]
// ============================================
// PART 13: PROMISIFY
// ============================================
function promisify(fn) {
return (...args) =>
new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
// ============================================
// PART 14: SEQUENTIAL
// ============================================
async function sequential() {
const a = await Promise.resolve(1);
const b = await Promise.resolve(a + 1);
const c = await Promise.resolve(b + 1);
return c;
}
sequential().then(v => console.log(v));
// [ 3 ]
// ============================================
// PART 15: PARALLEL
// ============================================
async function parallel() {
const [a, b, c] = await Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]);
return [a, b, c];
}
parallel().then(v => console.log(v));
// [ [ 1, 2, 3 ] ]
// ============================================
// PART 16: TIMEOUT PATTERN
// ============================================
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
withTimeout(
new Promise(r => setTimeout(() => r('done'), 2000)),
500
).catch(err => console.log(err.message));
// [ Timeout ]
// ============================================
// PART 17: RETRY PATTERN
// ============================================
async function retry(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
}
}
}
// ============================================
// PART 18: FALLBACK PATTERN
// ============================================
function withFallback(promise, fallback) {
return promise.catch(() => fallback);
}
withFallback(Promise.reject(new Error('x')), 'default')
.then(v => console.log(v));
// [ default ]
// ============================================
// PART 19: MAP WITH CONCURRENCY
// ============================================
async function mapLimit(items, limit, fn) {
const results = [];
for (let i = 0; i < items.length; i += limit) {
const chunk = items.slice(i, i + limit);
results.push(...await Promise.all(chunk.map(fn)));
}
return results;
}
mapLimit([1, 2, 3, 4, 5], 2, x => Promise.resolve(x * 10))
.then(r => console.log(r));
// [ [ 10, 20, 30, 40, 50 ] ]
// ============================================
// PART 20: FULL SCRIPT
// ============================================
const promise42 = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise42.then(value => {
console.log('Success:', value);
});
const failing42 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing42.catch(err => {
console.log('Error:', err.message);
});
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(values => console.log(values));
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('fail'))
]).then(results => console.log(results.map(r => r.status)));
Promise.race([
Promise.resolve('fast'),
new Promise(r => setTimeout(() => r('slow'), 100))
]).then(winner => console.log(winner));
Promise.any([
Promise.reject(new Error('a')),
Promise.resolve('b')
]).then(first => console.log(first));
Quick Reference
Promise States
| State | Meaning |
|---|---|
pending | Not settled yet |
fulfilled | Resolved successfully |
rejected | Failed with a reason |
Instance Methods
| Method | Purpose |
|---|---|
.then(onFulfilled, onRejected) | Handle success/failure |
.catch(onRejected) | Handle failure |
.finally(onFinally) | Always runs |
Static Methods
| Method | Purpose |
|---|---|
Promise.resolve(v) | Wrap value in resolved Promise |
Promise.reject(e) | Create rejected Promise |
Promise.all(arr) | All succeed or reject on first failure |
Promise.allSettled(arr) | Wait for all, never rejects |
Promise.race(arr) | First to settle wins |
Promise.any(arr) | First to fulfill wins |
Combinators
| Method | Resolves | Rejects |
|---|---|---|
all | When all fulfill | When any rejects |
allSettled | When all settle | Never |
race | First to fulfill | First to reject |
any | First to fulfill | When all reject |
Common Patterns
| Pattern | Code |
|---|---|
| Sequential | await a; await b; await c; |
| Parallel | await Promise.all([a, b, c]) |
| Timeout | Promise.race([fn(), timeout]) |
| Retry | Loop with try/catch |
| Fallback | promise.catch(() => fallback) |
| Promisify | new Promise((res, rej) => fn(...)) |
Chaining Rules
Return from .then() | Next .then() receives |
|---|---|
| Value | That value |
| Promise | Resolved value of that Promise |
| Throw error | Skips to .catch() |
| (nothing) | undefined |
Errors in Chains
| Where thrown | Where caught |
|---|---|
In executor | .catch() on that Promise |
In .then() | Next .catch() |
In .catch() | Next .catch() |
In .finally() | Next .catch() |
Best Practices
✅ Do This:
// Always add .catch()
fetch('/data')
.then(r => r.json())
.catch(err => console.error(err)); // ✅
// Use Promise.all for parallel
const [a, b] = await Promise.all([fa(), fb()]); // ✅
// Use allSettled for partial failure
const results = await Promise.allSettled(tasks); // ✅
// Use .finally for cleanup
promise.finally(() => hideSpinner()); // ✅
// Return Promises from .then for chaining
.then(user => fetch(`/orders/${user.id}`)) // ✅
// Use Promise.race for timeouts
Promise.race([fn(), timeout(ms)]); // ✅
// Use Promise.any for fallback sources
Promise.any([primary, backup]); // ✅
// Prefer async/await for readability
async function run() {
try { await fn(); } catch (e) { ... }
} // ✅
❌ Don’t Do This:
// Don't forget .catch()
fetch('/data').then(r => r.json()); // ❌ unhandled rejection
// Don't nest .then() unnecessarily
promise.then(a =>
b().then(b => c().then(c => ...))
); // ❌ use chaining
promise.then(a => b()).then(b => c()).then(c => ...); // ✅
// Don't create a Promise around an existing Promise
new Promise((res, rej) => fetch('/x').then(res, rej)); // ❌ anti-pattern
// Don't use the Promise constructor for sync code
new Promise(r => r(42)); // ❌ use Promise.resolve
// Don't mix callbacks and Promises
fn((err, res) => { ... }).then(...); // ❌ pick one
// Don't ignore the returned Promise
array.forEach(async x => await process(x)); // ❌ not awaited
await Promise.all(array.map(process)); // ✅
// Don't throw inside .then without .catch
promise.then(() => { throw new Error('x'); }); // ❌ unhandled
promise.then(() => { throw new Error('x'); }).catch(e => ...); // ✅
// Don't rely on .finally receiving the value
promise.finally(v => console.log(v)); // ❌ v is undefined
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
No .catch() | Unhandled rejection | Always catch |
Nested .then() | Callback hell returns | Flatten the chain |
| Promise constructor overuse | Anti-pattern | Use async |
forEach with async | Doesn’t await | for...of or map |
Missing await | Promise not settled | await or .then() |
finally gets no value | Doesn’t receive result | Capture before finally |
all fails fast | One failure = all fail | Use allSettled |
race on reject | Rejects if first rejects | Use any for first success |
| Floating promise | Silent failure | void or .catch() |
| Returning non-Promise | Chain works but odd | Promise.resolve |
Real-World Examples
1. Basic Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise.then(value => {
console.log('Success:', value);
});
// [ Success: done ]
2. Rejected Promise
const failing = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing.catch(err => {
console.log('Error:', err.message);
});
// [ Error: failed ]
3. Chaining
Promise.resolve(1)
.then(v => v + 1)
.then(v => v * 2)
.then(v => console.log(v));
// [ 4 ]
4. Error in Chain
Promise.resolve(1)
.then(v => { throw new Error('mid'); })
.then(v => console.log(v))
.catch(err => console.log(err.message));
// [ mid ]
5. Finally
Promise.resolve('ok')
.then(v => console.log(v))
.finally(() => console.log('Done'));
// [ ok ]
// [ Done ]
6. Promise.all
Promise.all([1, 2, 3].map(x => Promise.resolve(x * 2)))
.then(values => console.log(values));
// [ [ 2, 4, 6 ] ]
7. Promise.allSettled
Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('fail'))
]).then(results => {
console.log(results[0].status); // fulfilled
console.log(results[1].status); // rejected
});
8. Promise.race
Promise.race([
new Promise(r => setTimeout(() => r('slow'), 1000)),
new Promise(r => setTimeout(() => r('fast'), 100))
]).then(winner => console.log(winner));
// [ fast ]
9. Promise.any
Promise.any([
Promise.reject(new Error('a')),
Promise.resolve('b')
]).then(first => console.log(first));
// [ b ]
10. Promise.resolve
Promise.resolve(42).then(v => console.log(v));
// [ 42 ]
11. Promise.reject
Promise.reject(new Error('x')).catch(e => console.log(e.message));
// [ x ]
12. Fetch with Promise
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
13. Timeout Pattern
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
withTimeout(fetch('/slow'), 5000)
.then(res => console.log(res))
.catch(err => console.log(err.message));
14. Retry Pattern
async function retry(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
}
}
}
15. Parallel with Concurrency
async function mapLimit(items, limit, fn) {
const results = [];
for (let i = 0; i < items.length; i += limit) {
const chunk = items.slice(i, i + limit);
results.push(...await Promise.all(chunk.map(fn)));
}
return results;
}
16. Fallback Pattern
withFallback(Promise.reject(new Error('x')), 'default')
.then(v => console.log(v));
// [ default ]
17. Promisify
function promisify(fn) {
return (...args) =>
new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}
18. Parallel Data Load
async function loadAll() {
const [user, orders, products] = await Promise.all([
fetch('/user').then(r => r.json()),
fetch('/orders').then(r => r.json()),
fetch('/products').then(r => r.json())
]);
return { user, orders, products };
}
19. Sequential Data Load
async function loadSequential() {
const user = await fetch('/user').then(r => r.json());
const orders = await fetch(`/orders/${user.id}`).then(r => r.json());
const details = await fetch(`/orders/${orders[0].id}`).then(r => r.json());
return details;
}
20. Full Script
const promise42 = new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 1000);
});
promise42.then(value => {
console.log('Success:', value);
});
const failing42 = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('failed')), 1000);
});
failing42.catch(err => {
console.log('Error:', err.message);
});
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
Promise.all([p1, p2, p3]).then(results => {
console.log(results);
});
Promise.allSettled([p1, p2, p3]).then(results => {
results.forEach(r => console.log(r.status));
});
Promise.race([p1, p2]).then(first => {
console.log('First:', first);
});
Promise.any([p1, p2]).then(firstSuccess => {
console.log('First success:', firstSuccess);
});
Visual: Promise Lifecycle
┌──────────────────────────────────────────────┐
│ Promise lifecycle │
│ │
│ new Promise((resolve, reject) => { │
│ // async work │
│ }) │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ pending │ │
│ └────┬─────┘ │
│ │ │
│ ┌───┴───┐ │
│ │ │ │
│ ▼ ▼ │
│ fulfill reject │
│ │ │ │
│ ▼ ▼ │
│ .then() .catch() │
│ │ │ │
│ └───┬───┘ │
│ │ │
│ ▼ │
│ .finally() — always runs │
│ │
└──────────────────────────────────────────────┘
Visual: Promise Chaining
┌──────────────────────────────────────────────┐
│ Chained .then() │
│ │
│ Promise.resolve(1) │
│ │ │
│ ▼ │
│ .then(v => v + 1) → returns Promise(2) │
│ │ │
│ ▼ │
│ .then(v => v * 2) → returns Promise(4) │
│ │ │
│ ▼ │
│ .then(v => console.log(v)) → 4 │
│ │ │
│ ▼ │
│ .catch(err => ...) → skipped if no error │
│ │ │
│ ▼ │
│ .finally(() => ...) → always runs │
│ │
└──────────────────────────────────────────────┘
Visual: Combinators
┌──────────────────────────────────────────────┐
│ Promise.all([a, b, c]) │
│ │
│ a ────► ✓ │
│ b ────► ✓ │
│ c ────► ✗ → whole thing rejects │
│ │
│ Result: reject on FIRST failure │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Promise.allSettled([a, b, c]) │
│ │
│ a ────► ✓ │
│ b ────► ✗ │
│ c ────► ✓ │
│ │
│ Result: array of {status, value|reason} │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Promise.race([a, b]) │
│ │
│ a ────► slow (100ms) │
│ b ────► fast (10ms) ← winner │
│ │
│ Result: first to settle │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Promise.any([a, b, c]) │
│ │
│ a ────► ✗ │
│ b ────► ✓ ← first success │
│ c ────► (still pending) │
│ │
│ Result: first to SUCCEED │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Syntax | Example |
|---|---|---|
| Create | new Promise((res, rej) => {}) | new Promise(r => r(42)) |
| Resolve | resolve(value) | resolve('done') |
| Reject | reject(err) | reject(new Error('x')) |
| Success | .then(fn) | p.then(v => ...) |
| Failure | .catch(fn) | p.catch(e => ...) |
| Cleanup | .finally(fn) | p.finally(() => ...) |
| All | Promise.all(arr) | All or fail |
| All settled | Promise.allSettled(arr) | Always array |
| Race | Promise.race(arr) | First to settle |
| Any | Promise.any(arr) | First success |
| Resolve | Promise.resolve(v) | Wrap value |
| Reject | Promise.reject(e) | Rejected Promise |
Key takeaways:
- A Promise represents a value that will exist in the future
- It has three states: pending, fulfilled, rejected — and once settled, never changes
- The executor runs immediately;
resolveandrejectsettle the Promise .then()handles success;.catch()handles failure;.finally()always runs- Chaining returns a new Promise — return values propagate
- Throwing inside
.then()skips to the next.catch() Promise.allwaits for all or rejects on the first failurePromise.allSettlednever rejects — gives you an array of resultsPromise.racesettles with the first PromisePromise.anyresolves with the first success, rejects only if all failPromise.resolveandPromise.rejectwrap values- Always add
.catch()— unhandled rejections are bugs - Use
async/awaitfor cleaner syntax on top of Promises - Promises are the foundation of modern async JavaScript — master them
Remember: A Promise is a container for a future value. Create it, chain it, catch it, clean it up. Use Promise.all for parallel, allSettled for tolerant, race for timing, any for fastest success. Never forget .catch(). Never nest .then(). Always return from .then() when chaining. Master Promises, and the async world opens up — including async/await, which is built on top of them.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!