JS - Promises Basics
· 6 min read
JavaScript Promises
JavaScript Promises simplify asynchronous programming by managing the execution and resolution of tasks that take time to complete.
A Promise
in JavaScript 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); // Output: Success!
return "Continuing execution";
})
.then(msg => {
console.log(msg); // Output: Continuing execution
})
.catch(error => {
console.error("Error caught:", error); // Never executes
});
- The first promise resolves successfully with "Success!".
- The first .then() receives this value and logs "Success!".
- The second .then() continues execution and logs "Continuing execution".
- The .catch() never executes because there’s no error.
Case 2: Error Occurs (Handled by .catch())
If an error occurs at any stage of a promise chain, .catch()
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: Might fail...");
throw new Error("🔥 Step 3 Failure");
})
.catch(error => {
console.error("🚫 Caught an error in Step 3:", error.message); // error.message = "🔥 Step 3 Failure"
console.log("Handling error, but continuing...");
return "✅ Error handled at Step 3";
})
.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 at Step 3
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 (After Re-Throw):", 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")
console.log("🎉 All done!"); // Output: 🎉 All done!
});
Promise.all vs Promise.race vs Promise.any
- Promise.all (Wait for all, fail if any fails) – Resolves when all succeed, but fails immediately if any reject.
- Promise.race (First to settle wins) – Resolves or rejects with the first settled promise, whether success or failure.
- 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, whether fulfilled or rejected.
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
- Resolves with an array of objects detailing the outcome of each promise.
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);
// Simply call console. No Results array
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