Skip to main content

Command Palette

Search for a command to run...

Understanding JavaScript Promises: A Guide to the Promise API

Published
•5 min read•View as Markdown
Understanding JavaScript Promises: A Guide to the Promise API

Introduction-

Javascript is known for its non blocking,asynchronous nature, which allows multiple tasks to be performed simultaneously. Handling asynchronous tasks can be challenging especially when they depend on each other.Traditionally developers used callback functions to manage these ,but it often led to the "callback hell" (pyramid of doom) making code messy and hard to read . To solve this issue, promises were introduced.

What is a Promise?

A promise in javascript represents the eventual completion (or failure) of an asynchronous operation. It can be thought of as a placeholder for a value that will be available in the future.

-> There can be three states of the promise:-

(1)Pending -> The promise is still waiting for the result.

(2)Fulfilled -> The operation was successfull, and the promise has a result now.

(3)Rejected -> The operation failed, and the promise has an error.

Code for the creation of the promise:->

const promise = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("Operation succeeded");
  } else {
    reject("Operation failed");
  }
});

Once a promise is created , we can handle its result using .then() for a successful resolution and .catch() for handling errors.

promise
  .then(result => {
    console.log(result); // Operation successful
  })
  .catch(error => {
    console.log(error); // Operation failed
  });

Promise Chaining:-

When there is a need to execute two or more async operations back to back, where each subsequent operation starts only when the previous operation succeeds with the result of the previous step, promise chaining comes into the picture.

It allows to perform a series of asynchronous tasks in sequence without falling into callback hell.

-> For example, imagine we are making API calls where the second request depends on the result of the first one. Instead of nesting callbacks (which can lead to unreadable code), we can chain promises, ensuring that each operation waits for the previous one to complete successfully.

const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Data from API');
  }, 1000);
});

const processData = (data) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(`${data} processed`);
  }, 1000);
});

fetchData
  .then(result => {
    console.log(result); // Data from API
    return processData(result);
  })
  .then(processedData => {
    console.log(processedData); // Data from API processed
  })
  .catch(error => {
    console.error('Error:', error);
  });

Promise API:-

(1) Promise.all():-If we want to perform multiple asynchronous operation simultaneoulsy and wait for all of them to complete, then promise.all() is a great tool.

It takes an array of promises and returns a new promise that resolves when all the promises are fulfilled or rejects if any promise is rejected.

The parameter passes inside promise.all() is an iterable such as array of promises.

The return type of promise.all() is a promise that resolves an array of results when all the input promises are fulfilled and rejects as soon as input promises are rejected.

Use Case:->

(a) When weneed to wait for multiple promises to complete, and we want to proceed only when all promises have succeeded.

(b)Useful when the results of multiple asynchronous operations are needed together for further processing.

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.log(error);
  });

(2)Promise.allSettled():- This method returns a promise that resolves after all the given promises are settled(either fulfilled or rejected).Unlike promise.all() , it never rejects ,even if some of the promises are rejected. Instead it returns the array of objects describing the outcome of each promise whether they are fulfilled or rejected.

const promise1 = Promise.resolve("Success");
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve("Another success");

Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index + 1} fulfilled with value: ${result.value}`);
      } else {
        console.log(`Promise ${index + 1} rejected with reason: ${result.reason}`);
      }
    });
  });

Use Case :->

(a) When we need to wait for multiple promises to complete and you want to know the outcome of all promises, regardless of whether they succeeded or failed.

(b) Useful when we need to collect the results of multiple operations and handle each result separately.

(3)Promise.race() :- This method returns the promise that resolves as soon one of the promises which are passed as an argument settles(either fulfilled or rejected).

The result will be the outcome of the first promise that will be settled.

const promise1 = new Promise((resolve) => {
  setTimeout(() => {
    console.log('Promise 1 resolved');
    resolve('First');
  }, 500);
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('Promise 2 rejected');
    reject('Second');
  }, 300);
});
Promise.race([promise1, promise2])
  .then(result => {
    console.log(`Resolved: ${result}`);
  })
  .catch(error => {
    console.log(`Rejected: ${error}`);
  });

Use Case:->

(a) When we need to proceed as soon as the first promise settles, either fulfilled or rejected.

(b) Useful for scenarios where we are interested in the quickest result, such as handling a timeout or prioritizing faster responses.

(4) Promise.any():-It takes an iterable of promises that resolves as soon as any one of the input promise is fulfilled.If all the input promises are rejected then it rejects with an AggregateError that contains all the rejection reasons.

It will wait for the first resolved promise.

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('Promise 1 rejected');
    reject('Error from Promise 1');
  }, 100);
});
const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    console.log('Promise 2 resolved');
    resolve('Success from Promise 2');
  }, 200);
});
const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('Promise 3 rejected');
    reject('Error from Promise 3');
  }, 300);
});
Promise.any([promise1, promise2, promise3])
  .then(result => {
    console.log(`Resolved: ${result}`); 
  })
  .catch(errors => {
    console.log('All promises were rejected:', errors.errors);
  });

Use case:->

(a) When we need to proceed as soon as any one of the promises is fulfilled, but we want to ignore the rejections.

(b) Useful when we want to succeed with the result of the first successful promise and handle failures only if all promises fail.

More from this blog

Pragyan's blog

6 posts