JavaScript Promises

What Is a Promise?

In JavaScript, a Promise is an object that represents the future result of an asynchronous operation. It acts like a placeholder for a value that is not yet available but will be resolved (or rejected) at some point.

Promises help solve problems caused by callback hell by providing a cleaner, more manageable way to handle asynchronous code like API requests, timers, and file operations.


The Lifecycle of a Promise

A Promise has three possible states:

  1. Pending – The operation is still in progress.
  2. Fulfilled – The operation completed successfully.
  3. Rejected – The operation failed.

Once a Promise is fulfilled or rejected, it becomes settled and cannot change its state.


Creating a Promise

You create a Promise using the Promise constructor:

javascriptCopyEditconst myPromise = new Promise((resolve, reject) => {
  let success = true;

  if (success) {
    resolve("Operation successful");
  } else {
    reject("Operation failed");
  }
});
  • resolve(value) is called when the operation is successful.
  • reject(error) is called when there is an error or failure.

Consuming a Promise

You use .then() to handle a successful result and .catch() to handle errors:

javascriptCopyEditmyPromise
  .then((result) => {
    console.log("Success:", result);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

Practical Example with setTimeout

Let’s simulate fetching data from a server:

javascriptCopyEditfunction fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      let dataAvailable = true;

      if (dataAvailable) {
        resolve("Data fetched successfully");
      } else {
        reject("Failed to fetch data");
      }
    }, 2000);
  });
}

fetchData()
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    console.error(err);
  });

Here, fetchData returns a Promise that resolves after 2 seconds. This structure is far more readable than nested callbacks.


Promise Chaining

One of the biggest advantages of Promises is chaining, where each .then() passes its result to the next.

javascriptCopyEditfunction stepOne() {
  return Promise.resolve("Step 1 completed");
}

function stepTwo(prev) {
  return Promise.resolve(prev + " -> Step 2 completed");
}

stepOne()
  .then((result) => stepTwo(result))
  .then((finalResult) => console.log(finalResult))
  .catch((err) => console.error(err));

Output:

vbnetCopyEditStep 1 completed -> Step 2 completed

Handling Errors

You can catch errors at any point in the chain using .catch():

javascriptCopyEditnew Promise((resolve, reject) => {
  throw new Error("Something went wrong");
})
  .then((res) => {
    console.log("This won't run");
  })
  .catch((err) => {
    console.error("Caught an error:", err.message);
  });

This makes error handling simpler and centralized.


Promise.all() – Run Promises in Parallel

If you want to wait for multiple Promises to complete, use Promise.all():

javascriptCopyEditconst p1 = Promise.resolve("First");
const p2 = Promise.resolve("Second");

Promise.all([p1, p2]).then((results) => {
  console.log(results); // ["First", "Second"]
});

If any of the Promises fails, Promise.all() immediately rejects.


Promise.race() – First One Wins

javascriptCopyEditconst p1 = new Promise((res) => setTimeout(res, 100, "First"));
const p2 = new Promise((res) => setTimeout(res, 200, "Second"));

Promise.race([p1, p2]).then((result) => {
  console.log("Winner:", result); // "First"
});

Whichever Promise settles (resolves or rejects) first determines the outcome.


Conclusion

Promises are a powerful feature in modern JavaScript, allowing you to handle asynchronous operations more cleanly and avoid deeply nested callbacks.

Key Takeaways:

  • A Promise represents a value that may not be available yet.
  • Use .then() for success and .catch() for errors.
  • Promises can be chained for sequential tasks.
  • Use Promise.all() or Promise.race() for managing multiple Promises.

Mastering Promises is essential for working with APIs, user interactions, file systems, and any time-delayed operations. Once comfortable, you can move on to async/await, which is built on top of Promises for even cleaner syntax.