JavaScript Promises

A Promise is an object that represents the eventual result of an asynchronous operation, replacing deeply nested callbacks with a cleaner chain.

What is a Promise?

A Promise is a placeholder for a value that is not available yet but will be at some point in the future. Rather than passing a callback into an asynchronous function, you receive a Promise object and attach handlers to it. When the underlying task finishes, the Promise notifies those handlers with either the result or an error.

The three states of a Promise

StateMeaning
PendingThe operation has not finished yet
FulfilledThe operation succeeded and produced a value
RejectedThe operation failed with an error

A Promise starts out pending. Once it becomes fulfilled or rejected it is considered settled, and its state can never change again. You react to those outcomes with .then() for success and .catch() for errors.

Creating and consuming a Promise

<!DOCTYPE html>
<html>
<body>

<h2>Creating a Promise</h2>

<script>
const task = new Promise((resolve, reject) => {
  const ok = true;
  if (ok) {
    resolve("Task done");
  } else {
    reject(new Error("Task failed"));
  }
});

task
  .then((result) => console.log(result))   // "Task done"
  .catch((error) => console.log(error.message));
</script>

</body>
</html>

Chaining promises

Each call to .then() returns a new Promise, so you can chain steps one after another in a flat, readable sequence. If any step throws or rejects, control jumps to the nearest .catch(). This is what solves the nesting problem of callbacks.

Chaining steps with then and catch

<!DOCTYPE html>
<html>
<body>

<h2>Chaining Promises</h2>

<script>
fetch("https://api.example.com/user/1")
  .then((response) => response.json())
  .then((user) => {
    console.log(user.name);
    return user.id;
  })
  .then((id) => console.log("ID is " + id))
  .catch((error) => console.log("Something failed:", error))
  .finally(() => console.log("Done"));
</script>

</body>
</html>
Note: .finally() runs whether the promise fulfilled or rejected. It is ideal for cleanup work such as hiding a loading spinner.

Running promises together

  • Promise.all() waits for every promise to fulfill, or rejects as soon as one fails.
  • Promise.allSettled() waits for all promises and reports each result, success or failure.
  • Promise.race() settles as soon as the first promise settles.
  • Promise.any() fulfills with the first success, ignoring rejections until all fail.

Waiting for several promises at once

<!DOCTYPE html>
<html>
<body>

<h2>Promise.all</h2>

<script>
const p1 = Promise.resolve(10);
const p2 = Promise.resolve(20);
const p3 = Promise.resolve(30);

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values);        // [10, 20, 30]
  const total = values.reduce((a, b) => a + b, 0);
  console.log(total);         // 60
});
</script>

</body>
</html>
Note: Always attach a .catch() (or use try/catch with async/await). An unhandled rejection can crash a Node.js process and hides real bugs in the browser.