An Ultimate Guide to Javascript Promises

An Ultimate Guide to Javascript Promises

In this tutorial, we would be talking about all the JavaScript promises, their differences and their use cases.

Introduction

Promises are a way to implement async programming in JavaScript(ES6). A Promise will become a container for future value. In a layman's term, you place and order for an item on Amazon. After your order is placed you receive a receipt with delivery date in your mailbox . The receipt in this case stands as a promise that your order will be delivered to you. The receipt is like the container or a proof of the item you order(future value).

For asynchronous programming, JavaScript used callbacks but there is a problem with using callbacks which is callback hell.

Callback might seem ok for little applications but when it comes to complex application with a lot of callbacks, you begin to have a lot of deeply nested callback functions which might become very difficult to read, understand or mange.

An example of callback hell

func1(function(a){ 
 func2(a, function(b){ 
  func3(b, function(c){ 
   ... 
  }); 
 }); 
});

Promises to the Rescue

A Promise is basically created when we are unsure of whether or not the assigned task will be completed. The Promise object represents the eventual completion (or failure) of an async(asynchronous) operation and its resulting value. As the name suggests a Promise is either kept or broken. A Promise is always in one of the following states:

  • fulfilled: Action related to the promise succeeded.
  • rejected: Action related to the promise failed.
  • pending: Promise is still pending i.e not fulfilled or rejected yet.
  • settled: Promise has fulfilled or rejected

Syntax

const promise = new Promise((resolve,reject) => {....});

Example

const myPromise = new Promise((resolve, reject) => { 
 if (Math.random() > 0) { 
  resolve('Hello, I am positive number!'); 
 } 
 reject(new Error('I failed some times')); 
})

I published an article about "the new features in ECMAscript 2021" Where I talked about promise.any() 1 and people asked about the differences between Javascript promises. So here am I with another article to clear any confusion you might have about the various implementations of Javascript promises.

##Promise.All() The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. If any of the promises is rejected promise.all() throws and error with the first rejected promise

Let's take this example below. We create 3 promises to resolve at random times.

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("first promise resolved")
    // reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("second promise resolved")
    // reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("third promise resolved")
    // reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.all([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err)
  }

})();

If we take a look at our result in console we can see that all three promises resolves. Alt Text

Now what if one of our promises is rejected we have handled that error easily inside our try catch block

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("first promise resolved")
    // reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("second promise resolved")
    reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("third promise resolved")
    // reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.all([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err)
  }

})();

Taking a look at console we can see the rejected promise logged in console Alt Text

##Promise.race() The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise

Example

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("first promise resolved")
    // reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("second promise resolved")
    // reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("third promise resolved")
    // reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.race([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err)
  }

})();

if we check our console only one promise is returned and that's the promise that settles first either its resolved or rejected. Alt Text

Promise.any()

Promise.any() is like the opposite of Promise.all(). Promise.any() resolves if any of the supplied promised is resolved unlike promise.all() which waits for all promises to resolve before it resolves. lets take a look at the example below Basically we have 3 promises that resolves at random times. We have used setTimeout() function to set a time taken for each promise to resolve and we used Math.floor(Math.random) to give a random time to the setTimeout function so we really dont know which promise resolves first. This exaclty what happens in real world secenario.

const prom1 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the first promise"),
    Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the second promise"),
    Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the third promise"),
    Math.floor(Math.random() * 100)
  );
});

(async function() {
  const result = await Promise.any([prom1, prom2, prom3]);
  console.log(result); // Prints "A", "B" or "C"
})();

Alt Text The good thing about promise.any() is even if one of the promises is rejected it would continue to resolve other promise. It would only throw an aggregation error is all promises are rejected . Take a look at the two examples below In the first example only on promise is rejected but it continues to resolves. In the second example all there promises are rejected hence we get an aggregate error

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("first promise resolved")
    reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("second promise resolved")
    // reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("third promise resolved")
    reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.any([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err, 'all promises rejected')
  }

})();

Alt Text Example 2

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("first promise resolved")
    reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("second promise resolved")
    reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("third promise resolved")
    reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.any([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err, 'all promises rejected')
  }

})();

Alt Text

##promise.allSettled() The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise. In the example below one of the promises is rejected but promise.allSettled() still returns all settled promises.

const prom1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("first promise resolved")
    // reject("first promise rejected")
    }, Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("second promise resolved")
    // reject("second promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve("third promise resolved")
    reject("third promise rejected")
  }, Math.floor(Math.random() * 100)
  );
});

(async function() {
  try {
    const result = await Promise.allSettled([prom1, prom2, prom3]);
    console.log(result);
  } catch (err) {
    console.log(err)
  }

})();

Alt Text

Conclusion

In this tutorial we have been able to point out the difference between JavaScript Promises and what they do. If you have any additions or reservations let me know in the comment below