Q110: Promises, Thenables and the Resolution Procedure
What Interviewers Want To Evaluate
This question tests whether you understand promises beyond basic then and catch. Interviewers want promise states, settlement, chaining, thenable assimilation, microtask scheduling, error propagation, and why promise resolution is not the same as immediate fulfillment.
Senior answers should connect promise mechanics to async bugs, retry behavior, cancellation boundaries, and real UI state.
Short Interview Answer
A promise represents the eventual result of an asynchronous operation. It starts pending and eventually settles as fulfilled or rejected. Promise callbacks run as microtasks. The promise resolution procedure determines how a promise adopts another value, promise, or thenable. If a then callback returns a promise or thenable, the next promise waits for it. If it throws, the next promise rejects.
Detailed Interview Answer
A promise has three conceptual states:
pending
fulfilled
rejected
Once a promise settles, its state does not change.
const promise = Promise.resolve("ready");
promise.then((value) => {
console.log(value);
});
The callback runs later as a microtask, even if the promise is already fulfilled.
Settled vs Resolved
In everyday speech, people often use resolved to mean fulfilled. More precisely, resolution is the process of deciding what a promise should follow.
A promise can resolve to another promise and remain pending until that promise settles.
const slow = new Promise((resolve) => {
setTimeout(() => resolve("done"), 100);
});
const outer = Promise.resolve(slow);
outer adopts slow. It does not immediately fulfill with the promise object.
Thenable Assimilation
A thenable is any object with a callable then method.
const thenable = {
then(resolve) {
resolve("from thenable");
},
};
Promise.resolve(thenable).then(console.log);
JavaScript promise resolution assimilates thenables so they behave like promises.
This is powerful for interoperability, but it also means suspicious objects with then methods can influence async behavior.
Promise Chaining
Every call to then, catch, or finally returns a new promise.
fetchUser()
.then((user) => user.id)
.then((id) => fetchProfile(id))
.then((profile) => renderProfile(profile));
If a callback returns a plain value, the next promise fulfills with that value.
If a callback returns a promise, the next promise waits for it.
If a callback throws, the next promise rejects.
Error Propagation
Promise chains propagate errors until a rejection handler handles them.
fetchUser()
.then((user) => {
if (!user.active) {
throw new Error("Inactive user");
}
return user;
})
.catch((error) => {
console.error(error);
});
Throwing inside then is equivalent to returning a rejected promise for the next link.
Microtask Scheduling
Promise reactions run in the microtask queue.
console.log("A");
Promise.resolve().then(() => console.log("B"));
console.log("C");
The output is:
A
C
B
The promise callback runs after the current synchronous stack finishes.
Chaining Diagram
finally Behavior
finally runs whether the promise fulfills or rejects.
loadData()
.finally(() => {
setLoading(false);
});
If finally returns a rejected promise or throws, it can replace the outcome with a rejection.
Otherwise, the original value or reason passes through.
Promise.all
Promise.all fulfills when every input fulfills and rejects when the first input rejects.
const [user, settings] = await Promise.all([
fetchUser(),
fetchSettings(),
]);
Use it when all results are required.
Promise.allSettled
Promise.allSettled waits for every input and gives the result of each.
const results = await Promise.allSettled(tasks);
Use it when partial failure is acceptable and every result must be inspected.
Promise.race and Promise.any
Promise.race settles with the first settled input.
Promise.any fulfills with the first fulfilled input and rejects only if all inputs reject.
These are useful for timeouts, mirrors, fallback sources, and resilience patterns.
Common Mistakes
A common mistake is forgetting to return a promise from inside then.
doOne().then(() => {
doTwo();
});
The chain above does not wait for doTwo. Return it or use await.
Another mistake is thinking promises cancel work. Promises represent results; cancellation needs a separate mechanism such as AbortController.
Senior Trade-Offs
Promise mechanics matter when building retry systems, request deduplication, loading states, and error boundaries.
Avoid mixing deeply nested promise chains with async and await in a way that hides ownership of errors.
Code or Design Example
async function loadProfile(userId, signal) {
const [user, permissions] = await Promise.all([
fetch(`/api/users/${userId}`, { signal }).then((res) => res.json()),
fetch(`/api/users/${userId}/permissions`, { signal }).then((res) => res.json()),
]);
return { user, permissions };
}
This runs independent requests concurrently and lets AbortController handle cancellation.
Debugging Workflow
When promise behavior is wrong:
check missing returns in then chains
check whether errors are swallowed
check microtask ordering
check whether a thenable is being assimilated
check whether Promise.all should be allSettled
check whether cancellation needs AbortController
check loading state cleanup in finally
Interview Framing
Define states, settlement, and microtasks first. Then explain that promise resolution adopts promises and thenables, which is why chaining waits for returned async work.
Revision Notes
Promise callbacks run as microtasks. Promise resolution adopts returned promises and thenables. Errors propagate through chains until handled.
Key Takeaways
Promises are a scheduling and result-composition primitive. Senior understanding comes from knowing how values, errors, thenables, and microtasks move through a chain.
Follow-Up Questions
- What are promise states?
- What does settlement mean?
- Why does
thenrun asynchronously? - What is a thenable?
- What does promise resolution adopt?
- What happens if a
thencallback throws? - What happens if a
thencallback returns a promise? - What is the difference between
allandallSettled? - Do promises cancel work?
- When would you use
finally?
How I Would Answer This In A Real Interview
I would explain pending, fulfilled, rejected, and microtasks. Then I would say that promise resolution adopts returned promises or thenables, so chaining waits for async work. I would close with error propagation, Promise.all trade-offs, and cancellation through AbortController.