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