Altcademy - a Forbes magazine logo Best Coding Bootcamp 2023

What is Promise in JavaScript?

JavaScript has come a long way since its inception in 1995. It has evolved from a simple scripting language used to validate forms to a versatile language used for web applications, server-side programming, and even robotics. One of the many features that make JavaScript so powerful is its ability to handle asynchronous operations. This is where the concept of Promises comes in.

In this blog post, we will cover the following topics: - A brief introduction to asynchronous programming in JavaScript - What is a Promise? - How to create and use Promises - Chaining Promises - Error handling in Promises

Asynchronous programming in JavaScript

Asynchronous programming is a way of dealing with tasks that might take a long time to complete, without blocking the execution of the rest of your code. Imagine you're cooking dinner and you have to boil water, cook pasta, and make a sauce. You wouldn't just stand there waiting for the water to boil before starting the other tasks, would you? Instead, you would start boiling the water, then move on to the other tasks, and come back to the boiling water when it's ready.

JavaScript works in a similar way. When you have a long-running task like fetching data from a server, you don't want the rest of your code to be blocked, waiting for the data to arrive. Instead, you want to be able to continue executing other tasks and come back to the data fetching when it's ready.

This is where asynchronous programming comes in. It allows you to write code in such a way that it can perform long-running tasks concurrently, without blocking the main thread of execution.

What is a Promise?

A Promise in JavaScript is an object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. In simpler terms, it is a way to handle asynchronous operations in a more organized and readable manner.

Before Promises, developers used callbacks to handle asynchronous code. However, using callbacks could lead to a situation known as "callback hell," where the code becomes difficult to read and maintain due to too many nested callbacks.

Promises provide a cleaner and more structured way to handle asynchronous operations by allowing you to chain operations together and handle errors more gracefully.

Creating and using Promises

A Promise can be in one of three states: - pending: The initial state; neither fulfilled nor rejected. - fulfilled: The operation completed successfully, and the Promise has a resulting value. - rejected: The operation failed, and the Promise has a reason for the failure.

To create a Promise, you use the Promise constructor, which takes a single argument: a function called the "executor." The executor function takes two arguments: a resolve function and a reject function. The resolve function is used to fulfill the Promise with a value, while the reject function is used to reject the Promise with a reason.

Here's an example of creating a simple Promise:

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise fulfilled");
  }, 1000);
});

In this example, we create a new Promise that resolves with the value "Promise fulfilled" after a 1-second delay.

To use the value of a fulfilled Promise, you can use the .then() method, which takes a single argument: a function that will be called when the Promise is fulfilled, with the resulting value as its argument.

myPromise.then((value) => {
  console.log(value); // "Promise fulfilled"
});

If you want to handle the case where the Promise is rejected, you can use the .catch() method, which takes a single argument: a function that will be called when the Promise is rejected, with the reason for rejection as its argument.

const myRejectedPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("Promise rejected");
  }, 1000);
});

myRejectedPromise
  .then((value) => {
    console.log(value);
  })
  .catch((reason) => {
    console.error(reason); // "Promise rejected"
  });

Chaining Promises

One of the powerful features of Promises is the ability to chain them together. This means that you can perform multiple asynchronous operations one after the other, with each operation starting only when the previous one has completed.

The .then() method actually returns a new Promise, which can be used to chain additional operations. Here's an example of chaining Promises:

const add = (x, y) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (typeof x !== "number" || typeof y !== "number") {
        reject("Both arguments must be numbers");
      } else {
        resolve(x + y);
      }
    }, 1000);
  });
};

add(5, 3)
  .then((sum) => {
    console.log(sum); // 8
    return add(sum, 4);
  })
  .then((newSum) => {
    console.log(newSum); // 12
  })
  .catch((reason) => {
    console.error(reason);
  });

In this example, we have an add function that returns a Promise that resolves with the sum of its two arguments after a 1-second delay. We first call add with the arguments 5 and 3, and when the Promise resolves, we log the result (which is 8). We then call add again with the result and the argument 4, and when the second Promise resolves, we log the new result (which is 12).

Error handling in Promises

Handling errors in a Promise chain is simple and elegant. You can use the .catch() method to catch any errors that occur during the execution of the Promise chain.

In our previous example, if any of the add calls were to be rejected (for example, if one of the arguments was not a number), the .catch() method would be called with the reason for rejection, and the rest of the Promise chain would be skipped.

You can also use the .finally() method to execute a block of code regardless of whether the Promise chain was fulfilled or rejected. This can be useful for performing cleanup tasks or logging the end of the operation.

```javascript add(5, 3