Promises In Javascript

A javascript developer must comprehend the fundamental idea of promises in the language. The developer can use this in a variety of ways in their daily lives if the principle is understood.

On the internet, there are numerous articles and lessons concerning promises. However, only few of them serve as an all-inclusive manual for using promises. I'll make an effort to go into detail on promises in this article. Consequently, you won't need to consult any other sources.

What is a promise?

According to the MDN definition, a promise is an object that symbolises the eventual success or failure of an asynchronous operation as well as the value that results from it.

Why Do We Use Promises In JavaScript?

Javascript is a scripting language that is synchronous by definition. Promises are quite useful for carrying out asynchronous activities. Callbacks were frequently used before promises to manage several asynchronous tasks.

However, having too many callback routines resulted in callback hell, which is unmanageable code. This problem is resolved via promises.

That is a lot of specialised language, right? But if we take a non-technical approach to the issue, I believe you would better comprehend promises.

How Do Promises in Javascript Actually Work?

Promises made in javascript are comparable to those made in daily life.

Consider that you promised your partner that you would purchase her a pricey present. You are unsure of your ability to fulfil your pledge. You might be able to keep your word, or you might not.

Therefore, if you pledged but still couldn't afford to buy the gift, the promise is still outstanding. Your promise will have been kept if you are able to do so. However, if you are unable to do so due to whatever circumstance, your promise is in the refused condition.

When Was Promise Introduced in Javascript?

Promises are not a novel idea. In actuality, they have existed since 1976, the year the phrase was first used. jQuery deferred objects made the idea of it widespread at the beginning of 2011. Deferred objects have a similar notion to promises, but they do not adhere to all of the technical requirements outlined in the 2015 ECMA script for promises.

Finally, promises have been formally incorporated to the ECMA Script 2015 definition and have been integrated into Node Js and all of the newest browsers.

Different States In A Promise

The same ideas also apply to promises. Any one of the ensuing states applies to a promise. These are listed below:

  • Pending: The promise-related task hasn't been completed or rejected yet.
  • Fulfilled: Relating to the pledge, the assignment was successfully completed.
  • Rejected: The task connected to the pledge was unsuccessful.

The function that produces the promise is able to keep track of the promise states, which is a crucial thing to keep in mind.

Getting To Know More About The Promise Object

let isPossibleToPurchaseGift = true;
const giftPromise = new Promise(function(resolve, reject) {
  if(isPossibleToPurchaseGift) {
     resolve('You are able to keep your promise');
  } else {
     reject('You are unable to keep your promise');
  }
});
console.log(giftPromise);

The promise we made in the code above is resolved if the value of the variable "isPossibleToPurchaseGift" is set to true. Finally, we are showing the resolved state of the promise in the console window of the browser.

Static Methods in Promise Object

Promise.all(promises): It produces an array containing every promise's results after waiting for all promises to be fulfilled. Here, it's crucial to keep in mind that if any of the promises are broken, that constitutes the Promise's Error. All other findings are completely disregarded.

Promise.allSettled(promises): It is a relatively new technique. Its goal is to wait for all promises to be completed before returning the results as an array of objects with the status (which may be "fulfilled" or "rejected") and value (if fulfilled) or reason (if rejected).

Promise.race(promises): The outcome, or error, is determined by waiting for the first promise to resolve.

Promise.resolve(value): It creates a resolved promise with the given value.

Promise.reject(error): It creates a rejected promise that contains the specified error.

Creating A Promise In Javascript

let isPossibleToPurchaseGift = true;
const willGetNewGift = new Promise(function(resolve, reject) {
    if(isPossibleToPurchaseGift) {
      let gift = {
         ring: true,
         flowers: true
      };
       resolve(gift);
    } else {
         let error = new Error('Left my wallet!!');
       reject(error);
    }
});

In the code above, we have created a promise called "willGetNewGift". The promise constructor takes two parameters, first is resolve function and the second one is reject function.

What is Promise Resolve in Javascript?

In plain English, the resolve function says that if the promise is fulfilled, the promise object will be resolved with the specified value. As a result, the promise in the aforementioned code snippet will yield a gift object if the "willGetNewGift" variable is set to true.

What is Promise Reject in Javascript?

The promise object that is rejected by the reject function is returned along with an error message. If the "willGetNewGift" variable in the aforementioned code snippet is set to false, this promise will yield an error object.

Invoking The Promise In Javascript

const getNewGift = function() {
  willGetNewGift
    .then(function(gift) {
    console.log(gift);
    
  })
  .catch(function(error) {
    console.log(error.message);
  });
}; 
 
getNewGift();

The promise "willGetNewGift" is called in the code above, and the value of the fulfilled promise is obtained by using the then() function. True is entered into the variable "isPossibleToPurchaseGift". If the value is accurate, we consider the promise to have been fulfilled. So, inside the then() function, we are able to display the gift object. The full code is displayed below.

let isPossibleToPurchaseGift = false;
const willGetNewGift = new Promise(function(resolve, reject) {
  if(isPossibleToPurchaseGift) {
    let gift = {
      ring: true,
      flowers: true
    };
    resolve(gift);
  } else {
    let error = new Error('Left my wallet!!');
    reject(error);
  }
});
const getNewGift = function() {
  willGetNewGift
    .then(function(gift) {
      console.log(gift);
    })
    .catch(function(error) {
      console.log(error.message);
    });
};
getNewGift();

Chaining Promises in Javascript

Non-Technical Point of View

Let's say you promised your girlfriend a costly gift, then you said you'd want to go to dinner with her, and then you said you'd love to take a long drive together. Consider this scenario: You must keep your first commitment in addition to your second and third promises.

You would need to link together several promises in order to handle these types of events. Therefore, promise chaining is useful in these circumstances.

Technical Point of View

Asynchronous tasks can be carried out through the promise object in javascript. Each asynchronous task will return a promise object with a then method that can accept two parameters—a success handler and an error handler—and will be returned by every asynchronous task.

It is feasible to chain many promises together because the then method also returns a promise.

In the chain of promises, each handler (success or error) has the ability to return a value that will be used as a parameter by the following function.

The following handler won't be called until the previous one has completed its task if it returns a promise.

Let's justify what we said earlier with an example.

Implementing Promise Chaining in Javascript

let isPossibleToPurchaseGift = true;
const willGetNewGift = new Promise(function(resolve, reject) {
    if(isPossibleToPurchaseGift) {
      let gift = {
         ring: true,
         flowers: true
      };
       resolve(gift);
    } else {
         let error = new Error('Left my wallet!!');
       reject(error);
    }
});
const willAttendDinner = function(expensiveGift) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your promise by giving her an expensive ring';
    resolve(message);
  });
};
const willGoOnALongDrive = function(dinnerAttended) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your last promise by going on a long drive!';
    resolve(message);
  });
};
const getNewGift = function() {
  willGetNewGift
    .then(willAttendDinner)
    .then(willGoOnALongDrive)
    .then(function(longDrive) {
    console.log(longDrive);
  });
};
getNewGift();

We defined three distinct functions in the code snippet above; the first function, "willGetNewGift," returns a promise object, and the other two functions likewise return promises.

Please allow me to detail what occurred. The first function called is "willGetNewGift," which returns a promise. This promise object is then handed to the following method, "willAttendDinner," which also returns a promise object. Once more, the "willGoOnALongDrive" function receives that object. The function's output is then presented on the console as a last step. You will be able to read this message since you kept your last commitment by taking a lengthy journey.

What is Promise.all()?

Promise is a short word for it. When there are several promises and we need to wait for each one to finish before executing the next promise, the all() method is useful.

According to MDN documentation, after all of the promises supplied as an iterable have resolved or when the iterable is empty, the Promise.all() method delivers a single Promise. It rejects due to the initial promise being rejected.

The documentation makes it explicit that the entire Promise.all() method is refused if even one of the promise objects in the array is rejected.

How Does Promise.all() Work?

The MDN documentation informs us that the Promise.all() method requires an iterable object. Iterable objects are those that can be easily iterated. A couple of instances of these iterable things are strings and arrays.

This method typically provides a pending promise object that resolves or rejects asynchronously in response to the promise in the specified iterable object.

When the promises are provided to the promise all method after being successfully resolved, their respective values will remain there in the same sequence. All of the iterable's promises are discarded if even one of them is refused. Even if the remaining pledges are kept, this incident will still occur.

Implementing Promise.all() in Javascript

let isPossibleToPurchaseGift = true;
const willGetNewGift = function() {
   return new Promise(function(resolve, reject) {
    if(isPossibleToPurchaseGift) {
      let gift = {
         ring: true,
         flowers: true
      };
       resolve('You bought an expensive ring and flowers');
    } else {
       let error = new Error('Left my wallet!!');
       reject(error);
    }
  });
};
const willAttendDinner = function(expensiveGift) {
  return new Promise(function(resolve, reject) {
      let message = 'You kept your promise';
    resolve(message);
  });
};
const willGoOnALongDrive = function(dinnerAttended) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your last promise by going on a long drive!';
    resolve(message);
  });
};
const getNewGift = function() {
  Promise.all([
    willGetNewGift(),
    willAttendDinner(),
    willGoOnALongDrive()
  ]).then(function(result) {
    console.log(result);
  });
};
getNewGift();

Each of the three functions we generated in the piece of code above produces a promise object. The Promise.all() function was then used to call each of them, returning an array containing the promises' results.

The outcome will produce an error if any of the promises are unable to be fulfilled. The snippet of code is displayed below.

let isPossibleToPurchaseGift = false;
const willGetNewGift = function() {
   return new Promise(function(resolve, reject) {
    if(isPossibleToPurchaseGift) {
      let gift = {
         ring: true,
         flowers: true
      };
       resolve('You bought an expensive ring and flowers');
    } else {
       let error = new Error('Left my wallet!!');
       reject(error);
    }
  });
};
const willAttendDinner = function(expensiveGift) {
  return new Promise(function(resolve, reject) {
      let message = 'You kept your promise';
    resolve(message);
  });
};
const willGoOnALongDrive = function(dinnerAttended) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your last promise by going on a long drive!';
    resolve(message);
  });
};
const getNewGift = function() {
  Promise.all([
    willGetNewGift(),
    willAttendDinner(),
    willGoOnALongDrive()
  ]).then(function(result) {
    console.log(result);
  }).catch(function(error){
    console.log(error.message);
  });
};
getNewGift();

What is Promise.race()?

This function should be used if we need to immediately return the outcome of the first resolved promise or rejected promise.

According to MDN documentation, the Promise.race() method produces a promise that fulfils or rejects in accordance with the value or reason from the first promise in an iterable. 

Implementing Promise.race() in Javascript

let isPossibleToPurchaseGift = true;
const willGetNewGift = function() {
   return new Promise(function(resolve, reject) {
    if(isPossibleToPurchaseGift) {
      let gift = {
         ring: true,
         flowers: true
      };
      setTimeout(function(){
       resolve('You bought an expensive ring and flowers'); 
      }, 500);
       
    } else {
         let error = new Error('Left my wallet!!');
       reject(error);
    }
  });
};
const willAttendDinner = function(expensiveGift) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your promise';
     setTimeout(function(){
        resolve(message);
     }, 2000);
  });
};
const willGoOnALongDrive = function(dinnerAttended) {
  return new Promise(function(resolve, reject) {
    let message = 'You kept your last promise by going on a long drive!';
    setTimeout(function(){
       resolve(message);
    },3000);
  });
};
const getNewGift = function() {
  Promise.race([
    willGetNewGift(),
    willAttendDinner(),
    willGoOnALongDrive()
  ]).then(function(result) {
    console.log(result);
  }).catch(function(error){
    console.log(error.message);
  });
};
getNewGift();

We can observe from the code snippet above that only the willGetNewGift() method required 500 milliseconds to perform out of the three functions that return promise objects upon successful execution. As a result, after running this code block, the promise's outcome is returned.

Are Javascript Promises Synchronous or Asynchronous?

You should be aware that javascript is a single-threaded programming language at initially. The term "single-threaded" refers to the requirement that only one block of code be run at a time. Simply put, blocking is a feature of all JavaScript programming.

Sometimes we have to do activities, but we are unsure of the precise moment that the activity will be finished and the outcome returned. However, we also need to ensure that certain code blocks are run when we receive a successful result or, in the event of a failure, address that situation as well.

We must create javascript asynchronous codes to handle these circumstances. Promises enable for asynchronous programming. Therefore, it is evident that promises are asynchronous.

Let's justify with an example that promises are asynchronous.

let isPossibleToPurchaseGift = true;
// willGetNewGift promise definition
// willAttendDinner promise definition
// willGoOnALongDrive promise definition
const getNewGift = function() {
  console.log('Before giving gift');
  willGetNewGift
    .then(willAttendDinner)
    .then(willGoOnALongDrive)
    .then(function(longDrive) {
    console.log(longDrive);
  });
   console.log('After giving gift');
};
// call our promise
getNewGift();

Implementing Javascript Promises in Cleaner Way

The promise wrapper syntax is used in all of the examples in this article. Although there are many better methods to write promises effectively, we chose to use this syntax so that you could learn promises quickly. Writing promises using that method will make it much simpler to sustain commitments for challenging projects.

Please allow me to define promise wrapper. In a promise wrapper, you create codes that, based on whether a promise was successfully performed or not, resolve or reject a promise.

return new Promise(function(resolve, reject){
      // codes to execute
});

Above code snippet is the example of a promise wrapper.

Following code snippet explains how you can write promises in a better way.

let isPossibleToPurchaseGift = true;
//isPossibleToPurchaseGift = false;
const willGetNewGift = function() {
    if(isPossibleToPurchaseGift) {
       return Promise.resolve('It is possible to purchase gift');
    } else {
       let error = new Error('Left my wallet!!');
       return Promise.reject(error);
    }
};
const willAttendDinner = function(purchasedGift) {
//   purchasedGift = false;
  if(purchasedGift) {
    return Promise.resolve('It is possible to attend dinner');
  } else {
    return Promise.reject(new Error('Unable to attend dinner!!'));
  }
  
};
const willGoOnALongDrive = function(attendedDinner) {
//   attendedDinner = false;
  if(attendedDinner) {
    return Promise.resolve('It is possible to go on a long drive');
  } else {
    return Promise.reject(new Error('Unable to go on a long drive!!'));
  }
  
};
willGetNewGift()
  .then(willAttendDinner)
  .then(willGoOnALongDrive)
  .then(function(response){
  console.log(response);
}).catch(function(error){
  console.log(error.message);
});

Attempt uncommenting each of the statements that have been commented one at a time, then rerun the codes. You'll probably have no trouble understanding the differences.

Writing Javascript Promises With ES6/ES2015, ES7

The "let", "const," and "fat arrow" syntax were added in ES6 or ES2015. You can write promises more effectively with that. With ES6, we can improve on the preceding example. The snippet of code is displayed below.

const isPossibleToPurchaseGift = true;
// const isPossibleToPurchaseGift = false;
const willGetNewGift = ()=> {
    if(isPossibleToPurchaseGift) {
       return Promise.resolve('It is possible to purchase gift');
    } else {
       const error = new Error('Left my wallet!!');
       return Promise.reject(error);
    }
};
const willAttendDinner = (purchasedGift)=> {
//   purchasedGift = false;
  if(purchasedGift) {
    return Promise.resolve('It is possible to attend dinner');
  } else {
    return Promise.reject(new Error('Unable to attend dinner!!'));
  }
  
};
const willGoOnALongDrive = (attendedDinner) => {
//   attendedDinner = false;
  if(attendedDinner) {
    return Promise.resolve('It is possible to go on a long drive');
  } else {
    return Promise.reject(new Error('Unable to go on a long drive!!'));
  }
  
};
 
willGetNewGift()
  .then(willAttendDinner)
  .then(willGoOnALongDrive)
  .then(response =>console.log(response))
  .catch(error =>console.log(error.message));

If you uncomment the commented lines, you can tinker with the code sample more easily.

Async and await syntax were added with ES7. This would make it simpler for us to grasp after applying it to our ES6 code. Additionally, there is no need for the then and catch routines. Use Javascript's try...catch syntax for error management.

const isPossibleToPurchaseGift = true;
// const isPossibleToPurchaseGift = false;
const willGetNewGift = ()=> {
    if(isPossibleToPurchaseGift) {
       return Promise.resolve('It is possible to purchase gift');
    } else {
       const error = new Error('Left my wallet!!');
       return Promise.reject(error);
    }
};
const willAttendDinner = (purchasedGift)=> {
  // purchasedGift = false;
  if(purchasedGift) {
    return Promise.resolve('It is possible to attend dinner');
  } else {
    return Promise.reject(new Error('Unable to attend dinner!!'));
  }
  
};
const willGoOnALongDrive = (attendedDinner) => {
  // attendedDinner = false;
  if(attendedDinner) {
    return Promise.resolve('It is possible to go on a long drive');
  } else {
    return Promise.reject(new Error('Unable to go on a long drive!!'));
  }
  
};
async function callFunctions() {
  try {
    willGetGift = await willGetNewGift();
    attendDinner = await willAttendDinner(willGetGift);
    willGoOnALongDrive = await willGoOnALongDrive(attendDinner);
     console.log(willGoOnALongDrive); 
    
  } catch(error) {
    console.log(error.message);
  }

callFunctions();

Once more, I would suggest that you uncomment commented codes one at a time in order to better comprehend the code. You'll comprehend things better if you do this.

I sincerely hope that the majority of you find the approach covered here to be helpful. Thank you for reading, and please feel free to leave any comments or questions in the comments section below.

Post a Comment

0 Comments