cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Promises

Learn JavaScript promises as values for work that finishes later: pending, fulfilled, rejected, then, catch, finally, eager executors, and one-time settling.

By the end, promises should feel concrete
  • 01
    Read the three statesTell pending, fulfilled, and rejected promises apart and know that settling happens once.
  • 02
    Create a promiseUse new Promise with resolve and reject, plus Promise.resolve and Promise.reject shortcuts.
  • 03
    Attach handlersUse then, catch, and finally for success, failure, and cleanup without jumping ahead to chaining.

Promises are order tokens

The Callbacks lesson introduced a problem: when work finishes later, we often pass a function and hope someone calls it correctly. A promise gives that future result a stable object. Instead of handing away all control, your code receives a value now and attaches instructions to it.

Promise, in one sentence

A promise is a JavaScript object representing one future result: it starts pending, then becomes either fulfilled with a value or rejected with a reason.

Real-life analogyA promise is a McDonald's order token

You order and pay at the McDonald's counter, then get a token right away. While the kitchen prepares your food, you can wait; when the number appears, you collect it or hear that an item is sold out.

In real life: Pay and receive an order token
In JavaScript: new Promise(...) returns a promise immediately
In real life: The kitchen prepares your food
In JavaScript: The promise is pending
In real life: Your number appears; collect food
In JavaScript: The promise fulfills with a value
In real life: The counter says an item is sold out
In JavaScript: The promise rejects with a reason

Where the analogy stops: A real token can be reused for the next customer; a JavaScript promise settles once and stays settled.

This lesson stays focused on a single promise and a single set of handlers. The next lesson, Promise chaining, shows how returned values and returned promises connect multiple steps.

Pending, fulfilled & rejected

INTERACTIVE

A promise is always in exactly one state. It begins pending. From pending, it may fulfill with a value, or reject with a reason. The shared word for fulfilled and rejected is settled.

Promise states and transitions
StateMeaningWhat can happen next
PendingNo final result yet. Handlers can be attached while the work continues.It may become fulfilled or rejected.
FulfilledThe operation succeeded with a value, such as "order ready".It stays fulfilled forever.
RejectedThe operation failed with a reason, such as "sold out" or an Error.It stays rejected forever.
Real-life analogyA package tracker has the same shape

A tracking page is useful even before the package arrives because it names the not-yet state. Promises are useful for the same reason: your program can pass around “not ready yet” without losing track of what should happen later.

In real life: In transit
In JavaScript: Pending
In real life: Delivered
In JavaScript: Fulfilled with the package
In real life: Delivery failed
In JavaScript: Rejected with a reason

Where the analogy stops: A shipping company can update many intermediate statuses. A promise exposes only pending, fulfilled, or rejected.

Pending, fulfilled, or rejected?
  • A timeout promise before its timer fires
  • A data request that has returned JSON successfully
  • A request promise whose network connection failed
  • resolve("ready") has already run
  • reject("sold out") has already run
  • The executor has started a timer but the delay has not ended
Try it yourself
0 of 6 correct

Sort each scenario by the promise state it describes.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

The most important state rule is one-way movement. Once a promise is fulfilled or rejected, extra calls to resolve or reject do nothing. That one-time behavior is what makes late handlers safe: they all observe the same remembered result.

Creating promises

LIVE PROMISE

You create a custom promise with new Promise. The constructor receives an executor function. JavaScript calls that executor with two functions: resolve for success and reject for failure.

Order token playground: settle a real promise
Order promisePop out in the code editor (opens in a new tab)JavaScript
const order = new Promise((resolve, reject) => {  setTimeout(() => {    if (outcome === "fulfill") resolve("order ready");    else reject("sold out");  }, delay);}); order  .then((value) => log("then: " + value))  .catch((reason) => log("catch: " + reason))  .finally(() => log("finally: throw away the receipt"));
Runtimeidle
resultNo promise yet
delay700 ms
outcomefulfill
Handler log
  1. Choose an outcome, then press Order.
Try it yourself

No promise has been created yet. When you press Order, new Promise returns immediately and the executor starts the timer.

This playground uses a real Promise and a real setTimeout. Reset cancels any timer started by the current run.

The playground runs real promises in the browser. Choose “Resolve, then try reject” and notice the log: the first result wins. The later attempt is ignored because the promise has already settled.

Already-settled shortcutsPop out in the code editor (opens in a new tab)JavaScript
Promise.resolve("already ready")
  .then((value) => console.log(value));

Promise.reject("already failed")
  .catch((reason) => console.log(reason));

Promise.resolve(value) and Promise.reject(reason) are shortcuts for promises that are already settled. They are useful in tests, examples, and code that wants to return a promise-shaped result even when the answer is known immediately.

then, catch & finally

STEP THROUGH

Handlers are instructions attached to the order promise. then handles fulfillment, catch handles rejection, and finally handles cleanup either way.

Choosing the right promise handler
HandlerRuns whenReceives
then(onFulfilled)The promise fulfillsThe fulfillment value
then(onFulfilled, onRejected)Fulfillment or rejection at this exact pointValue for success, reason for failure
catch(onRejected)The promise rejectsThe rejection reason
finally(cleanup)Either fulfillment or rejectionNo value or reason; it is for cleanup
finally cleans up and passes through
Step 0 of 5Ready
Your turn: follow the blue line

finally is for cleanup. It does not receive the value, and a normal finally callback lets that value continue.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
  .finally(() => {    console.log("finally: wipe counter");  })  .then((value) => {    console.log("then: " + value);  });
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The finally callback above does not receive "noodles". It simply logs cleanup and lets the original value continue to the next then. If a finally callback throws, that new error takes over; the Error handling with promises lesson focuses on those paths.

Promises run eagerly

ORDER PUZZLES

A common misconception is that then starts a promise. It does not. The executor starts during new Promise. The callback passed to then runs later, as a microtask, after the current script finishes. The Event loop and Microtasks in depth lessons build the full scheduling model; this lesson uses the rule.

Executor now, then callback later
Step 0 of 8Ready
Your turn: follow the blue line

Predict the printed order. The executor is synchronous; the then callback is asynchronous microtask work.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const order = new Promise((resolve) => {  console.log("executor: kitchen starts");  resolve("tacos");});console.log("after new Promise");order.then((value) => {  console.log("then: " + value);});
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The token analogy is exact here: the kitchen starts cooking when you order, not when you look at your token. Attaching handlers is like deciding what to do when your number shows on the screen.

Already fulfilled still means later
Step 0 of 5Ready
Your turn: follow the blue line

Already fulfilled does not mean callback now. Step through the script, then the microtask.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
console.log("A: cup ordered");ready.then((value) => {  console.log("B: then gets " + value);});console.log("C: keep walking");
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Settled promises remember

A promise is not an event. If a click happens before you add a click listener, that old click is gone. If a promise fulfills before you add then, the promise remembers the value and still calls your handler later.

A late handler still sees the valuePop out in the code editor (opens in a new tab)JavaScript
const remembered = Promise.resolve("receipt #42");

setTimeout(() => {
  remembered.then((value) => {
    console.log("late handler saw " + value);
  });
}, 10);
A promise cannot settle twicePop out in the code editor (opens in a new tab)JavaScript
const order = new Promise((resolve, reject) => {
  resolve("ready");
  resolve("extra ready");
  reject("out of buns");
});

order.then(
  (value) => console.log("fulfilled: " + value),
  (reason) => console.log("rejected: " + reason),
);

That memory is one reason promises are easier to compose than plain callbacks. You can pass the promise object around without worrying that the result will disappear before a caller attaches a handler.

Where you’ll use promises

Promises appear whenever JavaScript needs to wait without freezing the page: timers, loading data, asking a user for permission, reading files in some environments, and many browser APIs. Later, async and await make promise code read more like normal top-to-bottom code, but promises are still underneath.

Loading state with cleanupPop out in the code editor (opens in a new tab)JavaScript
let loading = true;

loadMenu()
  .then((menu) => {
    renderMenu(menu);
  })
  .catch((error) => {
    showError(error);
  })
  .finally(() => {
    loading = false;
    hideSpinner();
  });

In real interfaces, finally is often where a loading flag turns off. The success path renders data. The failure path shows an error. Cleanup runs for both.

Tiny utility you will write often

A delay(ms) helper wraps setTimeout in a promise so other code can wait for a timer with promise handlers.

Common misconceptions

  • “then starts the promise.” No. The executor already ran during new Promise.
  • “Already fulfilled means the callback runs right now.” No. Promise handlers run later as microtasks.
  • “resolve returns the value from the promise.” No. resolve settles the promise; handlers receive the value later.
  • “A promise can be both fulfilled and rejected.” No. The first settle call wins forever.
  • “finally is where I read the value.” No. finally receives no value; use then or catch.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict the eager executor order

Without running it first, write the four letters in the order they print.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log("A");
const order = new Promise((resolve) => {
  console.log("B");
  resolve("C");
});
order.then((value) => console.log(value));
console.log("D");

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeWrap setTimeout in delay(ms)

    Create a promise-based timer helper. You do not need to reject for this exercise.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function delay(ms) {
      // return a promise that fulfills after ms
    }
    
    delay(500).then(() => console.log("done"));
      Exercise 3 · PracticeHandle a rejection with catch

      Predict exactly what this rejection handler prints.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      Promise.reject("out of soup")
        .catch((reason) => console.log("handled: " + reason));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeAdd finally for cleanup

        Predict the first log line. Then run it and notice that menu still passes through.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        let loading = true;
        Promise.resolve("menu")
          .finally(() => {
            loading = false;
            console.log("loading: " + loading);
          })
          .then((value) => console.log(value));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain why a promise cannot settle twice

          Which word prints, and why does the other branch not run?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const order = new Promise((resolve, reject) => {
            resolve("ready");
            reject("sold out");
          });
          order.then(
            (value) => console.log(value),
            (reason) => console.log(reason),
          );

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            7 QUESTIONS
            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is a promise while its async work is still unfinished?

              Choose an answer to see the explanation.

            2. Question 2 of 7What order does the already-fulfilled promise print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("one");
              Promise.resolve("two").then((value) => console.log(value));
              console.log("three");

              Choose an answer to see the explanation.

            3. Question 3 of 7When does a Promise executor run?

              Choose an answer to see the explanation.

            4. Question 4 of 7What prints when resolve wins before reject?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const p = new Promise((resolve, reject) => {
                resolve("first");
                reject("second");
              });
              p.then(
                (value) => console.log(value),
                (reason) => console.log(reason),
              );

              Choose an answer to see the explanation.

            5. Question 5 of 7Which handler is meant for cleanup that should happen on success or failure?

              Choose an answer to see the explanation.

            6. Question 6 of 7What is true about finally((value) => ...)?

              Choose an answer to see the explanation.

            7. Question 7 of 7Why can you attach then after a promise has already fulfilled?

              Choose an answer to see the explanation.

            Key takeaways

            • A promise is an object for one future result.
            • It starts pending and settles once: fulfilled with a value or rejected with a reason.
            • new Promise runs its executor immediately; handlers run later as microtasks.
            • then, catch, and finally describe success, failure, and cleanup.
            • Settled promises remember their result, so handlers attached late still work.

            A promise is JavaScript’s one-time order token for async work: pending now, fulfilled or rejected later, and safe to attach handlers to at any time.

            Up next: Promise chaining.

            CompleteFrontend Clear concepts. Working examples.