Free beginner tutorial

JavaScript Promises: then, catch and Promise.all()

A JavaScript Promise represents the eventual result of an asynchronous operation. Learn how to handle a fulfilled or rejected Promise with .then() and .catch(), and how to coordinate independent work with Promise.all().

Quick answer: A Promise starts pending, then becomes fulfilled with a value or rejected with a reason. Attach .then() for a successful result and .catch() for errors. Use await as another way to write code that waits for a Promise.

Handle a Promise with then and catch

Many browser APIs return Promises. The fetch() function is one example. Return the next asynchronous step from .then() so the following handler receives its result.

fetch("data/items.json")
  .then(response => {
    if (!response.ok) {
      throw new Error(`Request failed: ${response.status}`);
    }
    return response.json();
  })
  .then(items => {
    console.table(items);
  })
  .catch(error => {
    console.error("Could not load items:", error);
  });

The first .then() checks the HTTP status and returns another Promise for the parsed JSON. The next .then() receives the data. A rejection or thrown error in the chain reaches .catch().

Run independent Promises together

When two tasks do not depend on one another, Promise.all() can wait for both concurrently. It fulfills with results in the same order as the input array. If any input rejects, the returned Promise rejects.

async function loadPageData() {
  const [profileResponse, postsResponse] = await Promise.all([
    fetch("data/profile.json"),
    fetch("data/posts.json")
  ]);

  if (!profileResponse.ok || !postsResponse.ok) {
    throw new Error("One or more requests failed.");
  }

  const [profile, posts] = await Promise.all([
    profileResponse.json(),
    postsResponse.json()
  ]);

  return { profile, posts };
}

Use Promise.all() when each task can begin without another task’s result. If the second request needs a value from the first, await them in sequence instead. The example files profile.json and posts.json are included beside this lesson. Handle network and parsing errors at the call site.

Promise.all or Promise.allSettled?

Choose Promise.all() when the complete set of results is required and a single failure should reject the group. Choose Promise.allSettled() when you need to inspect every outcome, including tasks that failed, without stopping at the first rejection.

Promise questions

Does fetch reject when a server returns 404?

Usually, no. A response with an HTTP error status still fulfills the fetch Promise. Check response.ok and handle an unsuccessful status explicitly.

Do I need to create a Promise with new Promise?

Usually not when using an API that already returns a Promise. Create one only when adapting a callback-based operation or controlling a custom asynchronous task. Wrapping an existing Promise in another Promise often adds unnecessary complexity.

Practice: load two independent resources

Make two small JSON files beside an HTML page. Fetch both with Promise.all(), check each response’s ok property, and display the combined result. Then temporarily change one path and confirm your error handling explains what went wrong.

Open fetch examples through a local web server or the live site. Browsers often block fetch requests from pages opened with a file:// URL. Do not put private API keys in client-side code.