JavaScript Promises

Handle results that arrive later with then, catch and Promise.all.

Lesson 48 of 56Advanced13 min read

Some tasks take time: loading data from a server, reading a file or waiting for a timer. JavaScript doesn't stop and wait for them. It carries on with other work and deals with the result later. A Promise is an object that represents that future result: "I promise to give you a value, or an error, when it's ready."

Without this approach, a page would freeze every time it loaded data, and visitors couldn't scroll or click until the network replied. Promises let the page stay responsive. They can feel abstract at first, so this lesson uses a simple analogy and small examples before you use them for real with fetch.

The three promise states

A promise is always in one of three states:

  1. pending: still waiting.
  2. fulfilled: finished successfully, with a value.
  3. rejected: failed, with an error.

Once a promise is fulfilled or rejected, it's settled, and its result never changes again.

Using a promise with then, catch and finally

JavaScript
fetchSomething()
  .then(result => { /* success */ })
  .catch(error => { /* failure */ })
  .finally(() => { /* either way */ });
  • .then() runs when the promise is fulfilled and receives the value.
  • .catch() runs when it's rejected, or when anything earlier in the chain throws an error.
  • .finally() runs at the end either way, which makes it ideal for hiding a loading spinner.

Whatever a .then returns is passed to the next .then, so you can chain several steps one after another. Always end a chain with .catch(), or failures will go unnoticed.

Creating your own promise

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

Inside new Promise((resolve, reject) => { … }), call resolve(value) for success or reject(error) for failure. Most of the time you'll use promises that other functions give you, like fetch, but making one yourself helps you understand how they work.

Running several promises together

  • Promise.all([p1, p2]) waits for all of them and fails if any fails. The results arrive as an array in the same order.
  • Promise.allSettled([…]) waits for all of them and tells you which succeeded and which failed.
  • Promise.race([…]) settles with whichever finishes first.

Promise.all is useful when a page needs several pieces of data at once. Starting all the requests together is much faster than waiting for each one in turn.

JavaScript Promises Syntax

Promise basics

const p = new Promise((resolve, reject) => {
  // async work…
  resolve("done"); // or reject(new Error("failed"))
});

p.then(value => console.log(value))
 .catch(error => console.error(error))
 .finally(() => console.log("finished"));

JavaScript Promises Summary

  • A promise represents a result that will arrive later.
  • Promises are pending, then either fulfilled or rejected.
  • .then handles success, .catch handles errors and .finally runs either way.
  • Values returned from .then pass down the chain.
  • Promise.all runs several tasks at once and waits for them all.

JavaScript Promises Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

Promises don't block

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

console.log("1. Ordering pizza…");

wait(1000).then(() => {
  console.log("3. 🍕 Pizza arrived!");
});

console.log("2. Setting the table while we wait");
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1A helper that returns a promise which resolves after ms milliseconds.
  2. Lines 5 to 7Schedule what to do when the promise fulfils.
  3. Line 9Runs immediately. The code didn't stop and wait.
Example 2

Success, failure and chaining

Run it a few times. The "dice" sometimes fails.

function rollDice() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const n = Math.ceil(Math.random() * 6);
      if (n === 1) reject(new Error("Rolled a 1. You lose!"));
      else resolve(n);
    }, 300);
  });
}

rollDice()
  .then(n => {
    console.log("Rolled", n);
    return n * 10;
  })
  .then(points => console.log("Points:", points))
  .catch(error => console.log("❌", error.message))
  .finally(() => console.log("Game over"));
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 2Return a new promise.
  2. Lines 5 to 6Reject with an error on a 1, otherwise resolve with the number.
  3. Lines 12 to 15The first then gets the number and returns points…
  4. Line 16…which the next then receives.
  5. Line 17Any rejection skips to catch.
  6. Line 18finally always runs.
Example 3

Promise.all

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

console.log("Loading profile, posts and friends…");
const start = Date.now();

Promise.all([
  wait(500, { name: "Ola" }),
  wait(800, ["post 1", "post 2"]),
  wait(300, 42),
]).then(([profile, posts, friendCount]) => {
  console.log(profile.name, posts.length, "posts", friendCount, "friends");
  console.log("Took about", Math.round((Date.now() - start) / 100) * 100, "ms");
});
ConsoleEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 6 to 10Three tasks start at the same time.
  2. Line 10Results arrive together, in the same order, destructured.
  3. Line 12Total time ≈ the slowest task (800ms), not the sum (1600ms).

Common JavaScript Promises Mistakes

Forgetting to return in a chain

Don't
getUser()
  .then(user => { getPosts(user.id); }) // result lost!
  .then(posts => console.log(posts));   // undefined
Do
getUser()
  .then(user => getPosts(user.id))
  .then(posts => console.log(posts));

No catch

Don't
loadData().then(show);
Do
loadData().then(show).catch(err => showError(err));

Unhandled rejections show up as "Uncaught (in promise)" errors and the user sees nothing happen.

Expecting the value immediately

Don't
const data = fetchData(); // a Promise, not the data
console.log(data.name);  // undefined
Do
fetchData().then(data => console.log(data.name));
// or, inside an async function: const data = await fetchData();

JavaScript Promises in Real Projects

Promises power the network

fetch, reading files, camera access, location and database calls in Node.js all use promises in modern APIs. Most code today uses the friendlier async/await syntax, which is built on promises. That's the next lesson.

JavaScript Promises Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Handle success and failure.

loadScores()
  .(scores => console.log(scores))
  .(error => console.log("Failed:", error.message));
Predict the outputmedium

In what order are the numbers logged?

console.log(1);
Promise.resolve().then(() => console.log(2));
console.log(3);
Multiple choicemedium

What does Promise.all do if one promise rejects?

Fix the bugmedium

The chain should log Total: 30. A value is being lost. Fix it.

Promise.resolve(10)
  .then(n => {
    n * 3;
  })
  .then(total => console.log("Total:", total));
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • Logs "Total: 30"

JavaScript Promises Quiz

3 questions to check what you learned.

  1. Which state is a promise in while it's still waiting?

    • pending
    • fulfilled
    • rejected
    • loading
  2. What does .finally() do?

    • Runs only on success
    • Runs only on failure
    • Cancels the promise
    • Runs after the promise settles, whether it succeeded or failed
  3. Promise.all([wait(1000), wait(2000)]) takes about how long?

    • 1 second
    • 2 seconds
    • 3 seconds
    • 0 seconds

JavaScript Promises Challenge

Make a delay function

Write delay(ms, value) that returns a Promise which resolves with value after ms milliseconds.

Then use it: delay(200, "Ready!").then(msg => console.log(msg));

function delay(ms, value) {
  // return a new Promise…
}

delay(200, "Ready!").then(msg => console.log(msg));
ConsoleEdit the code, then press Run or Ctrl+Enter
Checks
  • delay returns a Promise
  • It resolves with the value
  • It actually waits
  • Logs "Ready!"