Compile Ready
Module 9 · Promises

The Promise Lifecycle

Intermediate13m read8m practice21m total
PromisesLifecycleMicrotasksThenables

Introduction

A Promise is a state machine for a future value. It starts pending, then settles exactly once as fulfilled with a value or rejected with a reason. Once settled, the result is immutable and every observer sees the same outcome.

The interview-grade mental model separates four moments: executor execution, settlement, thenable adoption, and reaction callbacks running later as microtasks.

Why This Matters

Most promise output puzzles become straightforward once you know that the executor runs synchronously, .then callbacks run as microtasks, resolve can adopt another promise or thenable, and only the first settlement attempt counts.

Theory

States and settlement

A promise has three observable states: pending, fulfilled, and rejected. Fulfilled and rejected are the two settled states. Settlement is one-way: the first effective resolve or reject wins, and later calls are ignored.

Executor now, reactions later

The executor passed to new Promise runs immediately and synchronously. Callback functions registered with .then, .catch, and .finally are promise reactions, so they run later as microtasks after the current call stack is empty.

Resolved is not always fulfilled

resolve(value) fulfils with ordinary values, but if value is a promise or thenable, the outer promise adopts that object's eventual outcome. A promise can therefore be resolved to a pending promise and still remain pending until the inner promise settles.

TermPrecise meaning
PendingNo final result yet.
FulfilledSettled successfully with a value.
RejectedSettled unsuccessfully with a reason.
SettledFulfilled or rejected.
ResolvedLocked to a value, promise, or thenable; may still be pending.

Thenable assimilation

A thenable is any object with a callable then property. Promise resolution calls that method and adopts the outcome, while still protecting settle-once semantics. Badly behaved thenables that call both callbacks cannot change the result after the first call.

Throws in the executor

If the executor throws before settlement, the promise rejects with that thrown value. If it throws after the promise has already settled, the throw is ignored for that promise.

Output-puzzle checklist

  1. Run all synchronous code, including promise executors.
  2. Note which promises settle and which reactions are queued.
  3. Drain microtasks after the stack empties.
  4. Run timers and other macrotasks only after microtasks.

Visual Diagrams

Promise state machine
                 resolve(value)
              +----------------+
              |                v
[ pending ] --+----------> [ fulfilled ]
     |
     | reject(reason) or executor throw
     v
[ rejected ]

fulfilled and rejected are settled states.
After settlement, later resolve or reject calls are ignored.

A promise transitions out of pending exactly once.

Executor now, reactions later
new Promise(executor)
        |
        v
executor runs synchronously
        |
        v
promise settles or remains pending
        |
        v
then and catch reactions run as microtasks after the stack is empty

Construction is synchronous; observation is asynchronous.

Code Examples

Settle-once semantics

Only the first settlement attempt matters. Later calls are ignored.

Loading…

Resolving to a thenable

Resolving with a thenable adopts the thenable's eventual outcome instead of storing the object as-is.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1console.log('A');
2
3const promise = new Promise(function (resolve) {
4 console.log('B');
5 resolve('C');
6 console.log('D');
7});
8
9promise.then(function (value) {
10 console.log(value);
11});
12
13console.log('E');

Predict the output #2

javascript
1const promise = new Promise(function (resolve, reject) {
2 resolve('first');
3 reject('second');
4 resolve('third');
5});
6
7promise.then(
8 function (value) {
9 console.log(value);
10 },
11 function (reason) {
12 console.log(reason);
13 }
14);

Coding Exercises

Promisify an error-first callback

Medium

Implement promisify(fn). The wrapped function should return a promise. The original fn receives a final callback shaped like callback(error, value); reject for a truthy error and fulfil with the value otherwise.

Interview Questions

1What are the states of a promise, and can a settled promise change state?

A promise starts pending, then settles exactly once as fulfilled or rejected. Fulfilled and rejected are final states; once settled, the result cannot change and later calls to resolve or reject are ignored.

Asked at:GoogleMicrosoftAmazon

Follow-ups

  • What is the difference between resolved and fulfilled?
  • When do `.then` callbacks run?
2What happens if a promise is resolved with another promise or thenable?

The outer promise adopts the inner promise or thenable's eventual outcome. It fulfils if the adopted value fulfils and rejects if the adopted value rejects. If the adopted promise is still pending, the outer promise is resolved but not yet fulfilled.

Quiz

1. Which statement about a promise executor is true?

2. A promise can be resolved but not fulfilled when it is resolved with...

Summary

  • Promises start pending and settle exactly once as fulfilled or rejected.
  • The executor runs synchronously; `.then`, `.catch`, and `.finally` callbacks run as microtasks.
  • `resolve` adopts promises and thenables, so resolved and fulfilled are not always the same moment.
  • After a promise settles, later resolve or reject attempts are ignored.

Cheat Sheet

States: pending → fulfilled or rejected. Fulfilled/rejected = settled.

Settle once: first resolve or reject wins.

Executor: runs synchronously.

Reactions: .then, .catch, .finally run as microtasks.

Resolve vs fulfil: resolve(thenable) adopts the thenable and may remain pending.