JS - Promises Basics
· 7 min read
Promises simplify asynchronous programming by managing the execution and resolution of tasks that take time to complete.
A promise has three states:
- Pending: Initial state before resolution or rejection.
- Fulfilled (Resolved): Successfully completed, carrying a result.
- Rejected: Failed operation with an error reason.
Chaining Promises
Correct Chaining
Promises allow sequential execution by passing results to the next .then() handler.
new Promise(resolve => setTimeout(() => resolve(1), 1000))
.then(result => {
console.log(result); // 1
return result * 2;
})
.then(result => {
console.log(result); // 2
return result * 2;
})
.then(result => {
console.log(result); // 4
return result * 2;
});
Incorrect Chaining
Each .then() operates on the same original promise without passing results forward.
let promise = new Promise(resolve => setTimeout(
() => resolve(1), 1000)
);
promise.then(result => { console.log(result); return result * 2 } ); // 1
promise.then(result => { console.log(result); return result * 2 } ); // 1
promise.then(result => { console.log(result); return result * 2 } ); // 1
// modified result value is not passed forward
Error Handling
Promise handlers have an implicit try...catch, automatically catching thrown errors.
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!
is same as:
new Promise((resolve, reject) =>
reject(new Error("Whoops!"))
).catch(alert); // Error: Whoops!
Handling Errors in Chained Promises
Case 1: No Errors (Success Case)
new Promise((resolve, reject) => {
resolve("Success!");
})
.then(result => {
console.log(result); // Success!
return "Continuing execution";
})
.then(msg => {
console.log(msg); // Continuing execution
})
.catch(error => {
console.error("Error caught:", error); // Never executes
});
Case 2: Error Occurs (Handled by .catch())
If an error occurs at any stage of a promise chain, the first .catch() after the error will captures it. The execution resumes after .catch() with the next .then(), allowing for graceful recovery.
The execution of .then(), .catch(), and .finally() follows the order in which they are defined.
- If an error occurs, it skips the remaining
.then()blocks until a.catch()is found. - A re-thrown error continues down the chain until another
.catch()handles it. .finally()always runs, regardless of success or failure..finally()does not affect the promise chain's result.
new Promise((resolve, reject) => {
console.log("Step 1: Starting...");
resolve("✅ Step 1 Complete");
})
.then(result => {
console.log(result); // Output: ✅ Step 1 Complete
console.log("Step 2: Proceeding...");
return "✅ Step 2 Complete";
})
.then(result => {
console.log(result); // Output: ✅ Step 2 Complete
console.log("Step 3: fail...");
throw new Error("Step 3 Failure");
})
.catch(error => {
console.error("Caught an error:", error.message); // error.message = "Step 3 Failure"
console.log("Handling error, but continuing...");
return "Error handled. Resuming";
})
.catch(error => {
// This will be **SKIPPED** because the previous `.catch()` handled the error
console.error("Second Catch Block: Won't run!");
})
.then(result => {
console.log(result); // Output: Error handled. Resuming
console.log("Step 4: Another risky operation...");
throw new Error("💥 Step 4 Error");
})
.catch(error => {
console.error("Caught another error in Step 4:", error.message); // error.message = "💥 Step 4 Error"
console.log("Re-throwing the error...");
throw error; // Re-throwing → Next `.catch()` will execute.
})
.catch(error => {
console.error("Second Catch Block:", error.message);
// error.message = "💥 Step 4 Error"
return "Error handled at Step 4";
})
.finally(() => {
console.log("Cleanup in Finally: This executes no matter what.");
return "This value is IGNORED";
})
.then(result => {
console.log(result); // Output: Error at Step 4 (NOT "This value is IGNORED")
});
Promise Comparison
- Promise.all (Resolves when all succeed, but fails immediately if any reject)
- Promise.race (First to settle wins) – Resolves or rejects with the first settled promise
- Promise.any (First success wins, failures ignored) – Resolves with the first successful promise, ignoring rejections.
- Promise.allSettled (Wait for all, never rejects) – Resolves with the results of all
| Method | Resolves When | Rejects When | Result Format | Best Used For |
|---|---|---|---|---|
all() | All promises complete successfully | If any promise fails | Array of resolved values | When every task must succeed |
allSettled() | After all promises finish, regardless of success or failure | Never rejects | Array of { status, value/reason } | When you need results of all tasks, even failed ones |
any() | As soon as one promise succeeds | If all promises fail | The first successful value | When only one success is needed |
race() | As soon as any promise finishes, success or failure | Same as resolves | Value or error from first finished promise | When you care about first result, no matter what |
resolve() | Immediately resolves with given value | Never rejects | The provided value (or promise result) | Converting non-promises to promises |
reject() | Never resolves | Immediately rejects with reason | The rejection reason | Simulating or forcing an error |
then() | When the original promise resolves | When the original promise rejects | Whatever the .then() handler returns | Chaining async operations |
Example Code
const p1_1s = new Promise(resolve =>
setTimeout(() => resolve(1), 1000)); // Resolves in 1s
const p2_2s_Error = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000)); // Rejects in 2s
const p3_3s = new Promise(resolve =>
setTimeout(() => resolve(3), 3000)); // Resolves in 3s
const p4_4s = new Promise(resolve =>
setTimeout(() => resolve(4), 4000)); // Resolves in 4s
// Promise.all - Waits for all to resolve
Promise.all([p1_1s, p3_3s, p4_4s])
.then(console.log)
.catch(console.error);
// Output after 4s: [1, 3, 4] (All resolved)
// Promise.all - Fails early if any reject
Promise.all([p1_1s, p2_2s_Error, p3_3s])
.then(console.log)
.catch(console.error);
// Output after 2s: Error: Whoops! (Fails early)
// Promise.race - First to settle (resolve or reject)
Promise.race([p1_1s, p3_3s, p4_4s, p2_2s_Error])
.then(console.log)
.catch(console.error);
// Output after 1s: 1 (Fastest promise wins)
// Promise.race - First to reject
Promise.race([p3_3s, p4_4s, p2_2s_Error])
.then(console.log)
.catch(console.error);
// Output after 2s: Error: Whoops! (First rejection wins)
// Promise.any - First successful promise (ignores failures)
Promise.any([p2_2s_Error, p3_3s, p4_4s])
.then(console.log)
.catch(console.error);
// Output after 3s: 3 (First success wins, failures ignored)
// Promise.allSettled - Waits for all, never rejects
Promise.allSettled([p1_1s, p2_2s_Error, p3_3s, p4_4s])
.then(console.log);
/* Output after 4s:
[
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: Error: Whoops! },
{ status: 'fulfilled', value: 3 },
{ status: 'fulfilled', value: 4 }
]
*/
Promise.all(promises) - All or Nothing
- Waits for all promises to resolve.
- If any fail, rejects immediately.
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 1000)),
new Promise(resolve => setTimeout(() => resolve(2), 2000)),
new Promise(resolve => setTimeout(() => resolve(3), 3000))
])
.then(results => JSON.stringify(results, null, 2))
.then(console.log);
// Results array
[ 1, 2, 3 ]
Promise.allSettled(promises) - Waits for All
Promise.allSettled([
new Promise(resolve => setTimeout(() => resolve(1), 1000)),
new Promise((_, reject) => setTimeout(() => reject("Error!"), 2000)),
])
.then(results => JSON.stringify(results, null, 2))
.then(console.log)
// Results array
[
{ "status": "fulfilled", "value": 1 },
{ "status": "rejected", "reason": "Error!" }
]
Promise.race(promises) - First to Settle
- Resolves or rejects based on the first settled promise.
Promise.race([
new Promise(resolve => setTimeout(() => resolve(1), 1000)),
new Promise((_, reject) => setTimeout(() => reject("Whoops!"), 500))
]).then(console.log).catch(console.log);
Whoops!
Promise.any(promises) - First Success Wins
- Resolves with the first successful promise.
- If all fail, rejects with an
AggregateError.
Promise.any([
new Promise((_, reject) => setTimeout(() => reject("Fail 1"), 1000)),
new Promise(resolve => setTimeout(() => resolve(2), 2000)),
new Promise(resolve => setTimeout(() => resolve(3), 3000))
]).then(console.log); // 2