What is a Promise in JavaScript? How do you resolve a Promise, and what are its use cases
A Promise is an object representing the eventual result of an async operation. States: pending → fulfilled or rejected (once, then immutable). Create with `new Promise((resolve, reject) => …)`. Use for: fetch, timers, file I/O, any async API — and chain with `.then` / `.catch` or await. Promises moved JS off callback hell.
What it is
A Promise is a placeholder for a value that arrives later. Its three states:
pending → fulfilled (with a value)
→ rejected (with a reason)Once settled, it never changes. You read its outcome via callbacks (.then / .catch / .finally) or with await.
Creating one
const wait = (ms) => new Promise((resolve, reject) => {
if (ms < 0) reject(new Error("invalid"));
else setTimeout(() => resolve("done"), ms);
});The executor function runs synchronously and gets resolve and reject. Call one to settle the Promise.
Static helpers
Promise.resolve(value)— already fulfilled.Promise.reject(reason)— already rejected.Promise.all(arr)— all-or-nothing aggregator.Promise.allSettled(arr)— collect all outcomes.Promise.race(arr)— first to settle wins.Promise.any(arr)— first to fulfill wins.
Consuming
wait(100)
.then((v) => console.log(v))
.catch((e) => console.error(e))
.finally(() => console.log("cleanup"));Or with await:
try {
const v = await wait(100);
console.log(v);
} catch (e) {
console.error(e);
} finally {
console.log("cleanup");
}Use cases
- HTTP requests (
fetch). - File I/O (
fs.promises). - Database queries.
- Timers (
new Promise(r => setTimeout(r, ms))). - User-driven async (animation frame, requestIdleCallback, ResizeObserver-driven measurement).
- Aggregating multiple async operations.
Why they replaced callbacks
// Callback hell
getUser(id, (err, user) => {
if (err) return cb(err);
getPosts(user.id, (err, posts) => {
if (err) return cb(err);
render(posts);
});
});
// Promises
getUser(id).then(getPosts).then(render).catch(handleErr);
// async/await
const user = await getUser(id);
const posts = await getPosts(user.id);
render(posts);Flatter code, single error path, composable.
Microtask vs macrotask
.then callbacks run on the microtask queue — before the next event loop tick's macrotasks (setTimeout). Means a chain of .thens runs before any I/O resolves.
Anti-patterns
- The "promise constructor anti-pattern" — wrapping an existing Promise in
new Promise. Just return it. - Forgetting to return from a
.then— breaks the chain. - Mixing callbacks and Promises in the same code path.
- Unhandled rejection (no .catch, no try/catch around await).
Interview framing
"A Promise is an object with three states: pending, fulfilled, rejected — settled exactly once and then immutable. Create with new Promise((resolve, reject) => ...); consume with .then / .catch / .finally or await. They replaced callback hell with composable async — same single error path, flatter code. Static helpers (Promise.all / allSettled / race / any) give you the aggregation primitives. .then callbacks run on the microtask queue, ahead of macrotasks. Avoid the constructor anti-pattern of wrapping a Promise in new Promise."
Follow-up questions
- •What's the microtask queue?
- •Compare callbacks, promises, async/await.
- •What's the constructor anti-pattern?
Common mistakes
- •Constructor anti-pattern.
- •Forgetting to return inside .then.
- •No .catch / try/catch.
Performance considerations
- •Microtask queue runs before next macrotask; long chains can starve I/O if you spin.
Edge cases
- •Resolving with another Promise — it chains.
- •Synchronous throw inside executor → reject.
- •Multiple resolve calls — only the first counts.
Real-world examples
- •Every fetch call, every db.query, every setTimeout wrapped, every modern web API that's async.