Complete JavaScript Course Module 12: Advanced: Asynchronous JavaScript

JavaScript Promises Explained

  • Beginner
  • Lesson 12.2
  • 5 min read
  • 53 of 76 in the course

In short

A JavaScript promise is an object that stands for a value you will get later. This lesson shows how to create promises, use then and catch, and run many at once. What is a promise in JavaScript? A promise is like the buzzer a café gives you after you order.

A JavaScript promise is an object that stands for a value you will get later. This lesson shows how to create promises, use then and catch, and run many at once.

What is a promise in JavaScript?

A promise is like the buzzer a café gives you after you order. You do not have your coffee yet, but you hold something that promises to tell you when it is ready, or when something went wrong.

A promise is always in one of three states:

StateMeaningCafé example
pendingstill waitingcoffee is being made
fulfilledfinished with a valuebuzzer rings, coffee is ready
rejectedfailed with an error"sorry, we are out of milk"

Once a promise is fulfilled or rejected, it is settled and never changes again.

How to create a promise with new Promise

You create a promise with new Promise(). You give it a function with two parameters: resolve (call it on success) and reject (call it on failure).

function orderCoffee(type) {
  return new Promise((resolve, reject) => {
    console.log(`Making a ${type}...`);
    setTimeout(() => {
      if (type === "latte" || type === "espresso") {
        resolve(`Your ${type} is ready`); // success
      } else {
        reject(new Error(`We don't make ${type}`)); // failure
      }
    }, 1000);
  });
}

orderCoffee("latte")
  .then((message) => console.log(message))
  .catch((error) => console.log("Problem:", error.message))
  .finally(() => console.log("Thank you for visiting"));

// Output:
// Making a latte...
// Your latte is ready        (after 1 second)
// Thank you for visiting
  • .then() runs when the promise is fulfilled. It receives the value.
  • .catch() runs when the promise is rejected. It receives the error.
  • .finally() runs in both cases. It is good for clean-up, like hiding a loading spinner.

Try orderCoffee("tea") and you will see Problem: We don't make tea instead.

Tip: Always reject with an Error object, like reject(new Error("...")). You get a clear message and a stack trace that shows where the problem started.

How to promisify setTimeout

"Promisify" means wrapping old callback code in a promise. A wait helper is the classic example, and you will use it often:

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(1500).then(() => console.log("1.5 seconds have passed"));

// Output (after 1.5 seconds):
// 1.5 seconds have passed

setTimeout calls resolve when the time is up, so the promise becomes fulfilled.

How does promise chaining work?

Every .then() returns a new promise. Whatever you return from a .then() is passed to the next one. If you return a promise, the chain waits for it. This turns the callback pyramid into a flat list of steps.

const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const getUser = (id) => wait(500).then(() => ({ id, name: "Maya" }));
const getOrders = (user) => wait(500).then(() => [{ id: 101, total: 49.99 }]);

let currentUser;

getUser(7)
  .then((user) => {
    currentUser = user;
    return getOrders(user); // return the next promise
  })
  .then((orders) => {
    console.log(`${currentUser.name} has ${orders.length} order(s)`);
  })
  .catch((error) => {
    console.log("Something failed:", error.message); // one catch for all steps
  });

// Output (after 1 second):
// Maya has 1 order(s)

Compare this to the callback hell example: the steps read from top to bottom, and a single .catch() handles an error from any step.

Common mistake: Forgetting return inside .then(). Without it, the next .then() gets undefined and does not wait for your inner promise.

How to run several promises at once

Sometimes tasks do not depend on each other. Then you can start them all together and wait for the results. JavaScript has four helpers. Each one takes an array of promises.

MethodFulfills whenRejects when
Promise.allall succeed (array of values)any one fails
Promise.allSettledall are finished (never rejects)never
Promise.racethe first one settles, success or failurethe first one to settle fails
Promise.anythe first one succeedsall of them fail
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

const fast = wait(300).then(() => "fast server");
const slow = wait(900).then(() => "slow server");
const broken = wait(500).then(() => { throw new Error("broken server"); });

Promise.all([fast, slow]).then((values) => console.log("all:", values));

Promise.allSettled([fast, broken]).then((results) => {
  results.forEach((r) => console.log("allSettled:", r.status));
});

Promise.race([fast, slow]).then((winner) => console.log("race:", winner));

Promise.any([broken, slow]).then((first) => console.log("any:", first));

// Output:
// race: fast server
// allSettled: fulfilled
// allSettled: rejected
// all: [ "fast server", "slow server" ]
// any: slow server
  • Promise.all is perfect when you need every result, like loading a user and their settings together.
  • Promise.allSettled gives you an object for each promise with status and either value or reason. Use it when some tasks may fail and that is OK.
  • Promise.race is useful for timeouts: race your task against a timer.
  • Promise.any is useful when you have backup sources and only need one.

Practice exercise

Write a function rollDice() that returns a promise. After 1 second, it picks a random number from 1 to 6. If the number is 1, reject with new Error("You rolled a 1!"); otherwise resolve with the number. Call it with .then(), .catch() and .finally(). Then use Promise.all to roll three dice at once.

Hint: Math.floor(Math.random() * 6) + 1 gives a number from 1 to 6.

Key takeaways

  • A promise represents a future value and is pending, fulfilled or rejected.
  • Create one with new Promise((resolve, reject) => { ... }).
  • .then() handles success, .catch() handles errors, .finally() always runs.
  • Return values from .then() to build a flat chain with one .catch().
  • Use Promise.all, allSettled, race and any to manage many promises.