Promises

Architecturally, a Promise is an object that acts as a placeholder for a future value. When we start an asynchronous task like a network request, the engine immediately gives us this object. Initially, it is empty. Later, it will be filled with data or an error.

The Three States

A Promise can be in exactly one of three states. The state transition is irreversible.

  1. Pending: The initial state. The operation is still in progress.
  2. Resolved: The operation completed successfully. The object holds the value.
  3. Rejected: The operation failed. The object holds the error.

The Constructor: Creating a Promise

We create a Promise using the new Promise() constructor. This constructor takes a single function argument called the Executor Function.

The Executor Function

Crucial Rule: The Executor Function runs Synchronously and Immediately.

When we write new Promise(...), the JavaScript engine executes the code inside that function instantly, on the Call Stack. It does not wait.

console.log("1. Start");
 
const myPromise = new Promise((resolve, reject) => {
  // This Executor runs IMMEDIATELY (Synchronously)
  console.log("2. Inside Executor");
 
  // This async part goes to the Web API
  setTimeout(() => {
    resolve("Data Loaded");
  }, 1000);
});
 
console.log("3. End");

Output:

1. Start
2. Inside Executor  <-- Proves the executor is synchronous
3. End

Note: Only the resolve() or reject() calls inside the asynchronous callback happen later. The executor function is executed now.


Regaining Control - Solving Inversion of Control

In previous archive, we saw the danger of passing a callback into a third-party function, giving them control. Promises flip this model. Instead of passing our logic into analytics.track(), the function returns a Promise object to us.

// Callback Approach (Risky)
analytics.track(data, function () {
  // We hope they call this...
});
 
// Promise Approach (Safe)
const trackingTask = analytics.track(data);
 
// WE decide when to listen. WE control the next step.
trackingTask.then(() => {
  chargeCreditCard();
});

We trust the standard Promise API (the browser), not the third-party library, to handle the execution of our code.


Consuming a Promise

Once we have a promise, we attach handlers to react when the state changes. These handlers are placed in the Microtask Queue.

  • .then(onSuccess, onFailure): Runs if the promise is Fulfilled.
  • .catch(onFailure): Runs if the promise is Rejected.
  • .finally(onSettled): Runs when the promise is settled (either way).

The Chaining Magic

The true power of Promises is Chaining. Every call to .then() returns a brand new Promise.

  • If we return a value from .then(), the new promise fulfills with that value.
  • If we return a new Promise, the chain pauses and waits for that new Promise to settle.

This allows us to flatten the Pyramid of Doom into a vertical, readable chain.

// Flat, readable chain
getUser(userId)
  .then((user) => {
    // Return a new Promise (async operation)
    return getOrders(user.id);
  })
  .then((orders) => {
    // We receive the result of getOrders here
    return getPayment(orders[0].id);
  })
  .then((status) => {
    console.log("Payment Status:", status);
  })
  .catch((err) => {
    // Catches errors from ANY step above
    console.error("Something went wrong:", err);
  });

5. Summary Table

FeatureDescription
Executor(resolve, reject) => ... Runs Synchronously.
ImmutabilityOnce settled (Resolved/Rejected), state cannot change.
Microtask.then callbacks go to the high-priority Microtask Queue.
Chaining.then returns a new Promise, allowing linear sequencing.

Stop and Think: What happens if we forget to return the promise inside a .then()?

taskA()
  .then(() => {
    taskB(); // Missing 'return'
  })
  .then(() => {
    console.log("Done");
  });

The chain is broken. The next .then() will run immediately because the first .then implicitly returns undefined (which is a value) instead of waiting for taskB to finish. taskB becomes a "floating" promise running in the background, detached from the chain.