Exploring JavaScript Promises in Depth

Exploring JavaScript Promises in Depth

Promises In JavaScript

JavaScript, being single-threaded, cannot run two pieces simultaneously. So one will have to wait for a specific task to end first. But by using callback functions, it manages asynchronous operations. But over time, promises became a better method in dealing with asynchronous tasks than callbacks because of Promises.

  • Make the async code easier to read.

  • Provide combined error handling.

  • Better control flow. You can have async actions execute in parallel or series. So in this blog, we will understand the concept of promises in better terms. Let's start

What are Promises? A promise is an asynchronous action that may complete at some point and produce value. It can notify anyone interested when its value is available.

A JavaScript Promise consists of three states:

vugz15i3sz2asv64clw3.jpg

Unresolved or pending:- A promise is pending when the result is not ready. It may occur because the current operation may in the run, or something is yet to be finished.

Resolved or Fulfilled:- A promise is resolved when the task assigned to the promise is completed, and the corresponding value is available, without any errors.

Rejected:- A promise is rejected when an error occurs.

Now that we know what a promise and familiar with its basic terminology, let's move forward.

Creating a Promise

Mostly we would be only indulging in consuming the promises, but it is helpful to know how to create one.

Syntax

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

We can create a new promise using the promise as a constructor; it expects a single argument, a callback, also known as executor function, which takes two callbacks, resolve and reject. The executor function is immediately executed when a promise is created. The promise is resolved by calling resolve and rejected by calling rejected.

Here is an example:-

const fifteen= new Promise((resolve,reject)=>{
const number=15
if(number===15){
resolve('Well Done')
}
else{
reject('Value not matched hence rejected')
})

The resolve and reject functions take one argument, which can be a string, boolean, number, array, or string.

Let's see another example:-

const promise = new Promise((resolve, reject) => {
  const randomNumber = Math.random();
  setTimeout(() => {
    if(randomNumber < .6) {
      resolve('All things went well!');
    } else {
    reject('Something went wrong');
  }
  }, 2000);
});

Here we are creating a new promise using the Promise constructor. The promise is either rejected or resolved after 2 seconds. Initially, when the promise is created, it will be in a pending state with the undefined value assigned.

After 2 seconds timer finishes, the promise is either resolved or rejected randomly, and the value will be passed down to the resolve or reject function.

There is one thing to keep in mind that resolve or reject can be invoked only once, and further implementation of these has no effect. For example:-


const promise = new Promise((resolve, reject) => {
  resolve('Promise resolved');  // Promise is resolved
  reject('Promise rejected');   // Promise can't be rejected
});

Consuming a Promise

As we already know how to create promises from previous sections, now we will see how to consume the already created promises. We can consume the promises by calling .then and .catch methods on the promise syntax:-

promise.then(successCallback, failureCallback)
promise.catch(failureCallback)

The successCallback is called when a promise gets resolved. It takes one argument, which is the value passed, to resolve(). The failureCallback is called when a promise gets rejected. It takes one argument, which is the value passed, to reject().

Let's take an example:-

const promise = new Promise((resolve, reject) => {
  const randomNumber = Math.random();
  setTimeout(() => {
    if(randomNumber < .6) {
      resolve('All things went well!');
    } else {
    reject('Something went wrong');
  }
  }, 2000);
});

promise
  .then((data) => {
     console.log(data); //prints data
   })
  .catch((error) => {
     console.log(error); // prints Error object
  });

Here if the promise gets resolved, the successCallback is called with the value passed to resolve(). We use catch() for handling errors inside the failureCallback callback of the then() callback.

Promise Chaining

When the result of a promise is another promise, we have to chain them to get the results.

isNumPrime(5)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.log(err))

In the above example, the method of handling asynchronous operations looks fine when we have a smaller number of promises, but it gets untidy and difficult to manage when there are more than that. Then async-await comes into the picture to resolve the problem that promises face. They help you to handle multiple promises very easily.

Conclusions

That's it for now. We have seen how to create promises and why promises are better than callbacks. It is essential to know both consuming and creation of promises and chaining of promises. Let me know any suggestions or feedback for this blog.