What is a Promise and how to use it in javascript?

·

5 min read

What is a Promise and how to use it in javascript?

What is a promise?

A Promise in JavaScript is like a placeholder for a value that might not be available yet. It's a way to deal with operations that take some time, like fetching data from the internet or reading a file. Instead of blocking the whole program and waiting for these operations to finish.

Imagine you order food at a restaurant. You don't just stand there waiting for the food to be ready; you continue with other things. The waiter gives you a promise (a note with your order number), and tells you, "When your food is ready, we'll bring it to your table." In the meantime, you can chat with friends or do whatever you want. When the food is ready, it gets delivered to your table, and you can enjoy your meal.

Common Challenges with Traditional Callback Approaches

In the early days of JavaScript, we would handle asynchronous operations with callback functions, but this approach created many challenges like callback hell or Pyramid of Doom.

Example:

Asynchronous tasks nested within callbacks could quickly turn code into a pyramid-like structure, making it challenging to read and maintain.

fetchData((data) => {
    processFirstStep(data, (processedData) => {
        processSecondStep(processedData, (finalResult) => {
            // ... and so on
        });
    });
});

Promise provides a more linear and readable way to overcome this problem.

Key Components of a Promise

States of a Promise:

  • Pending: A Promise starts in a pending state, indicating that the asynchronous operation is ongoing, and the final result is not yet available.

  • Resolved (Fulfilled): The Promise transitions to the resolved state when the asynchronous operation is successfully completed.

  • Rejected: If an error occurs during the asynchronous task, the Promise enters the rejected state, indicating that the operation did not succeed.

Creating a promise

  • A Promise is created using the Promise constructor, which takes a single argument—a function often referred to as the executor.

  • The executor function receives two parameters: resolve and reject. These are functions provided by the Promise API to signal the completion or failure of the asynchronous operation.

const myPromise = new Promise((resolve, reject) => {
    // Asynchronous operation logic
    if (operationSuccessful) {
        resolve(result);
    } else {
        reject(error);
    }
});

Callbacks: resolve and reject:

  • resolve: The resolve function is a callback used to fulfil a Promise. It indicates that the asynchronous operation is completed successfully, and the Promise transitions to the resolved state.

reject: The reject function is a callback used to reject a Promise. It signals an error during the asynchronous task, causing the Promise to enter the rejected state.

Using .then() for Resolution:

  • The .then() method is used to handle the resolved state of a Promise.

  • It takes a callback function as its argument, executed when the Promise is successfully resolved. The result of the resolution is passed as a parameter to this callback.

      myPromise.then((result) => {
          // Code to handle the resolved state and the result
      });
    

Using .catch() for Rejection:

  • The .catch() method is employed for handling the rejected state of a Promise.

  • It takes a callback function as its argument, executed when the Promise encounters an error during its execution.

  •     myPromise.catch((error) => {
            // Code to handle the rejected state and the error
        });
    

Using .finally() for clean up:

  • The .finally()method will be executed regardless of whether the Promise is resolved or rejected.

  • If the Promise is resolved, the code inside the finally block will be executed after the then block.

  • If the Promise is rejected, the code inside the finally block will be executed after the catch block.

  • This method is often used for cleanup or finalization tasks that need to be performed, such as:

    1. closing resources.

    2. releasing allocated memory.

    3. closing loading indicator.

let promise = new Promise((resolve, reject) => {
  // Simulating an asynchronous operation
  setTimeout(() => {
    // resolve("Promise resolved");
    reject(new Error("Promise rejected"));
  }, 1000);
});

promise
  .then((result) => {
    console.log("Resolved:", result);
  })
  .catch((error) => {
    console.error("Rejected:", error.message);
  })
  .finally(() => {
    console.log("Finally block executed regardless of resolution or rejection");
  });

Promise chaining

  • Promises can be chained together to sequence asynchronous operations.

  • The result of one Promise's resolution can be passed to the next .then() block, allowing for a more linear and readable flow.

  •     myPromise
            .then((result) => {
                // Code for the first resolution
                return modifiedResult;
            })
            .then((modifiedResult) => {
                // Code for the second resolution
            });
    
        // example:
        new Promise((resolve, reject) => {
          setTimeout(() => resolve(1), 1000); 
        }).then((result) =>  { 
    
          console.log(result); // 1
          return result * 2;
    
        }).then((result) => {
    
          console.log(result); // 2
          return result * 2;
        })
    

Conclusion

  • Promise helps deal with asynchronous operations in a more elegant way.

  • Promises help us avoid callback hell.

  • To create a promise, we use the Promise constructor. The constructor takes a function as an argument, called an executor. The executor function is responsible for completing the asynchronous operation and resolving or rejecting the promise.

  • The executor function is passed two callback functions as arguments: resolve and reject. We call resolve when the asynchronous operation is successful, and reject when it fails.

  • We can use the then() method to handle the successful completion of the promise, and the catch() method to handle any errors that occur.

  • The.finally() method will be executed regardless of whether the Promise is resolved or rejected.

  • Promises can be used to chain together asynchronous operations.