What is a Promise?

JavaScript

readTime

6 min

What is a Promise?

If you program in JavaScript, you have certainly encountered the concept of Promise.

It’s a mechanism that helps manage asynchronous code, which is crucial in applications that must operate smoothly, even while fetching data, communicating with a server, or performing other background tasks.

In this article, you'll learn what a Promise is, how it works, and how to use it to handle asynchronous operations.

Of course, everything will be accompanied by examples! πŸ˜„


What is a Promise? πŸ€”

A Promise is an object in JavaScript that "promises" (hence the name!) to deliver the result of an asynchronous operation in the future. This result can be:

  • Fulfilled – the operation completed successfully, and we have a result.
  • Rejected – the operation failed.
  • Pending – the operation is ongoing, and we don’t yet have the result.

In practice, a Promise allows us to write asynchronous code more clearly than using callbacks, which was popular before Promises were introduced in the ES6 standard.


Creating a Promise in JavaScript ✨

To create a Promise, we use the Promise constructor.

A Promise takes a function as an argument, which has two parameters: resolve and reject. Resolve is a function that will be called if the operation completes successfully, and reject if it fails.

Example:

javascript
const promise = new Promise((resolve, reject) => {
  let success = true;

  if (success) {
    resolve("The operation succeeded!");
  } else {
    reject("The operation failed.");
  }
});

In the above example, we create a Promise that simply checks the success variable.

If it is true, the Promise is fulfilled, and the resolve function is called; otherwise, the reject function is called.

Asynchronous Operations in JavaScript and Promises βš™οΈ

A Promise is an essential tool for handling asynchronous operations in JavaScript.

Operations such as fetching data from a server, waiting for an API response, or handling timeouts with setTimeout are asynchronous operations that often take a while to complete.

Example of an asynchronous operation:

javascript
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data has been fetched!");
    }, 2000);
  });
};

fetchData().then((result) => {
  console.log(result); // "Data has been fetched!"
});

Here, the Promise simulates an asynchronous operation using setTimeout, and after 2 seconds, it returns a message that the data has been fetched.


How Does a Promise Work? πŸ”„

A Promise operates on the principle that a particular operation will either succeed or fail.

When the operation is ready, the Promise is either fulfilled or rejected, allowing us to call the appropriate callbacks to handle the result.

The .then() Method – Working with Promises πŸ’ͺ

When an asynchronous operation succeeds, we use the .then() method to pass the result for further processing.

Then takes a function as an argument, which will be executed when the Promise is fulfilled.

Example:

javascript
fetchData().then((result) => {
  console.log(result); // "Data has been fetched!"
});

In this case, the resolve function passes the result to then, and we can then display or further process it.


Error Handling – The .catch() Method πŸ›‘

Not all asynchronous operations succeed. When a Promise is rejected, we can use the .catch() method to handle the error.

Example:

javascript
const fetchDataWithError = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Error: Failed to fetch data!");
    }, 2000);
  });
};

fetchDataWithError()
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error); // "Error: Failed to fetch data!"
  });

Here, we simulate an operation that fails and call catch to handle the error.


Promise Chaining – Sequential Operations πŸ› οΈ

One of the powerful features of Promises in JavaScript is the ability to chain operations.

This allows us to sequentially perform multiple asynchronous operations in a clear way.

Example:

javascript
fetchData()
  .then((result) => {
    console.log(result);
    return fetchData();
  })
  .then((nextResult) => {
    console.log(nextResult);
  })
  .catch((error) => {
    console.error("An error occurred:", error);
  });

In the example above, we first perform one asynchronous operation, and then another once the first one completes.

If any operation is rejected, the result is handled in the catch block.


The Promise API and Modern Operations in JavaScript πŸš€

The Promise API was introduced in JavaScript as part of ES6 to simplify managing asynchronous operations.

Additionally, various methods are available to make working with Promises even easier.


Promise.all() – Running Multiple Operations in Parallel 🚦

The Promise.all() method allows multiple Promises to execute in parallel and returns the results once all operations are complete.

This is useful when we need to perform multiple asynchronous operations and wait for their combined results.

Example:

javascript
const promise1 = new Promise((resolve) =>
  setTimeout(resolve, 1000, "Promise 1 fulfilled")
);
const promise2 = new Promise((resolve) =>
  setTimeout(resolve, 2000, "Promise 2 fulfilled")
);

Promise.all([promise1, promise2]).then((results) => {
  console.log(results); // ["Promise 1 fulfilled", "Promise 2 fulfilled"]
});

Promise.race() – A Race Between Promises ⏱️

The Promise.race() method works similarly to Promise.all() but returns the result of the first Promise that is either fulfilled or rejected.

Example:

javascript
const promise1 = new Promise((resolve) =>
  setTimeout(resolve, 1000, "Promise 1 fulfilled")
);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 500, "Promise 2 rejected")
);

Promise.race([promise1, promise2])
  .then((result) => {
    console.log(result); // "Promise 2 rejected"
  })
  .catch((error) => {
    console.error(error);
  });

In this case, Promise.race() returns the result of the Promise that completes first.


Error Handling in Promises – Ensuring Application Stability πŸ’ͺ

When working with Promises, error handling is especially important. Sometimes asynchronous operations may fail, such as due to lack of internet connection or server issues.

Therefore, it’s always worth implementing catch to ensure proper exception handling.

Example:

javascript
fetchData()
  .then((result) => {
    console.log(result);
    return fetchData();
  })
  .catch((error) => {
    console.error("An error occurred:", error);
  })
  .finally(() => {
    console.log("Operation completed");
  });

The finally block is executed regardless of whether the Promise was fulfilled or rejected. This is an ideal place to clean up resources or display a final message.


Async/Await – Modern Asynchronous Management πŸ”₯

Promises are great, but an even more elegant way to handle asynchronous operations is async/await.

Async and await are special keywords that make Promises look and work more like synchronous code, greatly simplifying programming.

Example with async/await:

javascript
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Error fetching data:", error);
  }
}

Here, you no longer need to use .then() or .catch() because async/await makes the code more linear and intuitive.


Summary: Promises – The Key to Asynchronous Operations in JavaScript πŸ”‘

Promises are the foundation of asynchronous operations in JavaScript.

With them, you can handle operations such as data fetching or API interactions in a readable and efficient way.

Understanding how Promises work, how to create them, and how to handle errors is essential for building modern web applications.

Use Promises to make your applications more responsive and better at managing asynchronous operations! πŸ˜„

authorImg

Witek Pruchnicki

I passionately share knowledge about programming and more in various ways.