Introduction to Promises
Introduction
A promise is an object representing the eventual result of an asynchronous operation. It starts pending, then settles exactly once as either fulfilled with a value or rejected with a reason.
Promises replaced many callback patterns because they standardize completion, support chaining, and move error propagation into the chain.
Why This Matters
Promises are the foundation under async/await, fetch, modern testing utilities, and almost every JavaScript async abstraction. Interviewers expect you to know not only the states, but also that .then callbacks are microtasks and the promise executor runs synchronously.
Theory
Promise states
A promise has three conceptual states:
- Pending: the operation has not settled yet.
- Fulfilled: the operation completed successfully with a value.
- Rejected: the operation failed with a reason.
Once settled, a promise cannot change state. Calling resolve or reject again has no effect.
Executor versus reactions
The function passed to new Promise(executor) runs immediately and synchronously. The callbacks passed to .then, .catch, and .finally run later as microtasks.
Chaining
.then always returns a new promise. If the handler returns a plain value, the next promise fulfills with that value. If it throws, the next promise rejects. If it returns another promise, the next promise waits for that promise to settle. This is the rule that lets promises flatten async sequences.
Error propagation
A rejection skips fulfillment handlers until a rejection handler is found. A .catch is shorthand for .then(undefined, onRejected). After a catch handles the error and returns normally, the chain becomes fulfilled again.
Promise is eager, not lazy
Creating a promise with new Promise starts the executor immediately. It is not a lazy recipe. If you need laziness, wrap promise creation in a function and call that function later.
Promise limitations
Native promises do not include cancellation by themselves, and they represent one completion, not repeated events. Use AbortController for cancellable Web APIs and events or streams for repeated values.
Visual Diagrams
resolve(value)
Pending ------------------------> Fulfilled
|
| reject(reason)
v
Rejected
Settled promises do not change state again.
A promise settles exactly once.
then handler returns value -> next promise fulfills with value then handler throws error -> next promise rejects with error then handler returns promise -> next promise adopts that promise's eventual state
Every `.then` returns a new promise, enabling flat composition.
Code Examples
Creating and consuming a promise
The executor starts immediately. The then handler runs as a microtask after the current stack.
Error propagation through a chain
Throwing inside a .then rejects the promise returned by that .then.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1 2const promise = new Promise(function (resolve) {3 console.log('executor');4 resolve('value');5});6 7promise.then(function (value) {8 console.log(value);9});10 11console.log('after');Predict the output #2
1 2Promise.resolve()3 .then(function () {4 console.log('first');5 return 'second';6 })7 .then(function (value) {8 console.log(value);9 });10 11setTimeout(function () {12 console.log('timer');13}, 0);14 15console.log('sync');Predict the output #3
1 2Promise.resolve('A')3 .then(function (value) {4 console.log(value);5 throw new Error('boom');6 })7 .catch(function () {8 console.log('caught');9 })10 .then(function () {11 console.log('done');12 });13 14console.log('sync');Coding Exercises
Run promise tasks sequentially
MediumImplement runSequentially(tasks) where tasks is an array of functions. Each function returns a promise. Run them one after another and return a promise that fulfills with an array of results in order.
Interview Questions
1What are the states of a promise?
A promise starts pending and then settles exactly once as fulfilled with a value or rejected with a reason. After settlement, further attempts to resolve or reject are ignored.
2What is the difference between the promise executor and a `.then` callback?
The executor passed to new Promise runs synchronously at construction time. A .then callback is a promise reaction and runs later as a microtask after the current call stack clears.
Follow-ups
- What happens if a `.then` handler returns a promise?
- What happens if it throws?
Quiz
1. What does `.then` return?
2. When does the function passed to `new Promise(function (resolve) { ... })` run?
Summary
- A promise represents one eventual fulfillment or rejection.
- Promise executors run synchronously; reactions run as microtasks.
- `.then` returns a new promise and supports flattening by returning another promise.
- Errors thrown in a chain become rejections and can be handled by `.catch`.
Cheat Sheet
States: pending → fulfilled or rejected; settled once.
Executor: synchronous.
Reactions: .then, .catch, .finally are microtasks.
Chaining: return value → fulfill next; throw → reject next; return promise → adopt it.
Catch: handles rejection and can turn the chain back to fulfilled.