Back to Blog

ES6 and Beyond - Part 2

May 04, 2019
#es6+
  
#javascript
  
#intermediate
  
#promises
  

This post is second part of the previous one. If you haven’t read it yet, read it here:

ES6 & Beyond - Part 1

In this article, we are going to learn about promises and the new & improved/alternate way to achieve asynchronous task using async/await instead of promise.

The objectives are to learn following:

  • Promise
  • Promise.prototype.finally
  • async/await
  • for...await...of

Promise: An Introduction

So, what is promise?

It is an object, which guarantees us to return a single value some time in future for an asynchronous operation.

Let’s understand it a bit more by understanding the states that a promise can have. A promise is an object (mentioned above) can have 3 states:

  • fulfilled: When promise is resolved/fulfilled.
  • rejected: When a promise failed to get fulfilled.
  • pending: Neither rejected not fulfilled is the pending state.

Now here is the syntax on how to write a promise in javascript:

// default to pending state when it is constructed
const promise = new Promise((resolve, reject) => {
// do some asynchronous task (preferably)
let worked = true;
if (worked) {
// fulfilled state
resolve('Hurrrray!, I fulfilled the promise.');
} else {
// rejected state
reject(new Error('Sorry that promise did not get fulfilled.'));
}
});

An explanation on above code.

  • Line 2: We are creating/constructing a promise object. state would be pending and result will undefined
  • Line 7: Promise is getting resolved here so the state would be fulfilled with a returned value of the string inside resolve.
  • Line 10: Promise is getting rejected here. The state would be rejected and sets the result to error with the error value provided.

Now, we can use the promise object as shown below:

promise.then(
  success => console.log(success),
  error => console.log(error)
);

So, promise provides then-ability which is what guarantees us to get the data once a promise is resolved/rejected.

Finally, we can derive some rules regards to Promise:

  • A promise provides then() method.
  • A pending promise can result to either ‘fulfilled’ or ‘rejected’ state
  • A fulfilled or rejected promise is considered as settled. So, it must not transition to any other state.
  • Once a promise is settled, it should have a value which must not change.

A bit more on then is needed. Right? Ok, let’s first look at the syntax of then:

promise.then(onFulfilled[, onRejected]);
  • onFulfilled: Callback for resolved/fulfilled promise
  • onRejected (optional): Callback for rejected promise

One of the great thing about promise is chaining

To achieve the chaining, we use then() method provided by promise. Let’s have a look at following example.

const promise1 = promiseOne()
const promise2 = promise1.then(onFulfilled, onRejected)
// OR
const promise = promiseOne().then(onFulfilled, onRejected)

So, each promise represents the completion of another asynchronous step in the chain.

Before promise arrived, we fall pray of classic callback pyramid of doom. Let’s have a look:

getResultOne(function(resultOne) {
  getResultTwo(
    resultOne,
    function(resultTwo) {
      getFinalResult(
        resultTwo,
        function(finalResult) {
          console.log('phewwww, got the final result: ', finalResult);
        },
        onFailureCallback
      )
    },
    onFailureCallback
  )
}, onFailureCallback);

But, with promise, we use the callback on returned promise instead to form the promise chain which makes code better and more readable.

getResultOne()
  .then(resultOne => getResultTwo(resultOne))
  .then(resultTwo => getFinalResult(resultTwo))
  .then(finalResult => console.log('Wow, got the final result: ', finalResult))
  .catch(onFailureCallback);

catch looks new here, right? Ok. so catch is another method provided by promise which catches any kind of error that might have happened while promise tries to fulfil.

It is completely possible to chain after catch using then.

Promise.prototype.finally

This is again part of promise Promise.prototype.finally. It is useful when we wants to execute some sort of code regardless the outcome of the promise (be it fulfilled or rejected). Syntax is given below.

const promise = new Promise(/*syntax explained above*/)
promise
  .then(/*explanation above*/)
  .catch(/*callback to handle error */)
  .finally(() => {
    // promise is settled here...
    // could be either fulfilled or rejected though
  });

Let’s move to our next point which is async/await

async/await

async/await is nothing more than syntactic sugar for promises that we learnt above. Let’s look at the syntax first:

async function functionName(params) {
  // code goes here
}

Let’s say we need to fetch json file from a url. Here is how we will write it using promise.

const getJson = url => fetch(url).then(json => console.log(json))
// call the getJson method
getJson('https://jsonplaceholder.typicode.com/todos/1')

Now, let’s have a look on how we can use async/await for this.

const getJson = async url => {
  const json = await fetch(url);
  console.log('Json data: ', json);
}
getJson('https://jsonplaceholder.typicode.com/todos/1');

Both implementation are doing the same thing but async/await is more readable. As I have mentioned before about chaining, promise/then makes code less readable when we start doing the chaining but with async/await, you get more neat and clean code. Example given below to explain it. Here, I have created three promises:

const getResultOne = () =>
  new Promise(resolve => setTimeout(() => resolve('result one'), 2000));
const getResultTwo = () =>
  new Promise(resolve => setTimeout(() => resolve('result Two'), 1200));
const getFinalResult = () =>
  new Promise(resolve => setTimeout(() => resolve('result final'), 1500));

Now have a look at the usage of promise and async/await

The promise way

const getResult = () =>
  getResultOne()
    .then(resultOne => {
      console.log(resultOne);
      return getResultTwo(resultOne);
    })
    .then(resultTwo => {
      console.log(resultTwo);
      return getFinalResult(resultTwo);
    })
    .then(finalResult => {
      console.log('Wow, got the final result: ', finalResult);
    });
getResult();

The async/await way:

const getResult = async () => {
  const resultOne = await getResultOne();
  console.log(resultOne);
  const resultTwo = await getResultTwo(resultOne);
  console.log(resultTwo);
  const finalResult = await getFinalResult(resultTwo);
  console.log('Nice and easy....final result: ', finalResult);
}
getResult();

So, which one you prefer. Let me know in the comment section. Personally, I like async/await. It makes code more readable in synchronous way which leads to more manageable code.

Tip:

await can only be used inside async function.

Go ahead and do practice this at your own. Have questions, feel free to drop a comment with your question.

for…await…of

This one is related to await that we learnt just now. This helps us to iterate over async iterable objects as well as on sync iterables which includes built in string, array or array like objects (eg. arguments). Syntax:

for await (let item of iterable) {
    // code goes here...
}

Let’s take an example. Suppose, we need to fetch data from 3 urls. The urls are stored in an array.

const urls = [
  'https://robohash.org/robot1',
  'https://robohash.org/robot2',
  'https://robohash.org/robot3'
];
const getRobots = async function() {
  arrRobots = urls.map(url => fetch(url));
  for await (let robot of arrRobots) {
    console.log(`Status: ${robot.status} for url ${robot.url}`);
  }
}
getRobots();

I am sure you also realise how clean it is and how useful it can be. So, practice, practice and practice.

Summary

Today we learnt about Promise. The newer and nicer way to use the Promise by using async/await. We also looked at finally along with how to interate on await using for...await...of. Some of the terminology to remember are:

  • Promise
  • resolve/fulfilled
  • reject
  • settled
  • chaining
  • then
  • catch
  • finally
  • async
  • await
  • for…await…of

There are chances that you have questions around some of the points above which is not explained. It is kind of done intentionally so that you can come up with questions and also start exploring at your own. For any questions, drop a comment and own awesome community folks or myself will help you to get it answered.

Here are two questions from me for you to answer.

Q1: How to use multiple promises in parallel?

Q2: How can we solve Q1 with async/await

Thanks for reading. Happy learning.

Anand Kumar
Manager at PublicisSapient
Back to Blog