promises in js
Promises in JavaScript
Asynchronous lifecycle states, microtask queue scheduling, and chain composition.
The Busy Espresso Pager Buzzer
A Promise is a proxy for a value that may not be known when the promise is created. It replaced nested callback hell with composable, chainable asynchronous pipelines.
Three Immutable States
`pending` (initial state), `fulfilled` (operation succeeded with a value), `rejected` (operation failed with a reason/error). State changes are one-way and permanent.
The Synchronous Executor
The function passed to `new Promise((resolve, reject) => {})` executes synchronously immediately upon instantiation.
Microtask Queue Prioritization
Callbacks registered via `.then()`, `.catch()`, or `.finally()` do not run immediately; they are scheduled on the Microtask Queue, which executes before any setTimeout or DOM rendering.
Chaining and Flattening
Every `.then()` returns a brand new Promise. Returning a promise inside a `.then()` automatically unwraps it, avoiding nested hierarchies.
Understanding Promise chaining, error bubbling, and microtask timing:
console.log("1. Start script");
const fetchUserData = new Promise((resolve, reject) => {
console.log("2. Executor runs synchronously");
resolve({ id: 101, username: "dev_alex" });
});
fetchUserData
.then(user => {
console.log("4. User received:", user.username);
return user.id; // Returns a promise resolving to 101
})
.then(id => {
console.log("5. Querying permissions for ID:", id);
})
.catch(err => {
console.error("Caught error in chain:", err);
})
.finally(() => {
console.log("6. Pipeline cleanup finished");
});
console.log("3. End of synchronous script");
1. Start script
2. Executor runs synchronously
3. End of synchronous script
4. User received: dev_alex
5. Querying permissions for ID: 101
6. Pipeline cleanup finished
The Broken Chain (Missing Return)
The Risk: Inside `.then(res => { doWork(res); })`, forgetting to `return` results in the next `.then()` receiving `undefined`.
The Fix: Always return values or nested promises from `.then()` handlers to pass data down the chain.
The Promise Constructor Antipattern
The Risk: Wrapping an already promise-returning API inside `new Promise((resolve) => fetch().then(resolve))` adds unnecessary complexity and error leaks.
The Fix: Directly return the promise: `return fetch(url).then(res => res.json())`.
- ✓ 3 states: `pending` → `fulfilled` OR `rejected` (Settled states are immutable).
- ✓ Executor runs synchronously; `.then()/.catch()` handlers run asynchronously as Microtasks.
- ✓ Every `.then()` returns a new Promise.
- ✓ Unhandled rejections crash Node.js and trigger `unhandledrejection` events in browsers.
- ✓ Use `Promise.all()`, `Promise.allSettled()`, `Promise.race()`, or `Promise.any()` for concurrent workflows.