Promises, Async/Await

Promises, Async/Await

1. Promises

According to Merriam Webstar dictionary, a promise is a declaration to do something specified. Now, in JavaScript promises are similarly the same, a promise is made to resolve something, and on success, proceed to do something else, but in case the promise is not resolved a fallback scenario is provided.

1.1. Execution Status of Promises

Promises return back an execution status to the calling function to let them know what their current state of execution is.

These execution states are:

  • Pending: This is the state when execution is still under processing.
  • Resolved: This is the state when the promise is fulfilled, the resolution is returned back to the calling function.
  • Rejected: This is the state when something has gone wrong, the error is returned back to the calling function.

1.2. Creating a Promise

A promise is simply created by calling the constructor. A callback is also passed to a Promise with the resolve and reject status as parameters.

When a promise is resolved, then the function that is passed to the .then will be invoked. Whereas, when a promise is rejected, the function passed to the .catch will be invoked.

Syntax:

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

});
promise.then(success);
promise.catch(error);

function success() {
//what to be done on success
}

function error() {
//what to be done error
}

1.3. Promise chaining

In my previous article, I mentioned that the concept of promises was introduced to avoid callback hell by presenting a much cleaner code style.

Taking the same example discussed here, whereby:

  1. We want the user to input data
  2. Using the data given by the user in (1), we fetch some data from an API
  3. Next we fetch more data using data received in (2)
  4. And finally we display this data

Using promise chaining to achieve a much cleaner code style, we'll have:

    getUserInputData()
        .then(res => fetchUserData1(res))    //returns a promise, the next .then called
        .then((res => fetchUserData2(res))    //returns a promise, the next .then called
        .then((res) => displayData(res))
        .catch((err) => console.log(err));    //when any of the above promises is rejected, the function passed to .catch is invoked

Now, this is much cleaner and easier to understand compared to the callback alternative of achieving the same.

2. Async/ Await

async/await introduced in ES8, presents a much more elegant way of writing asynchronous code.

Taking the same example used in the promise chaining section above; we'll have:

async function user(){
    try {
        let userData = await InputData();
        let userData1 = await fetchUserData1(userData);
        let userData2 = await fetchUserData1(userData1);
        let displayDataUI = displayData(userData2);            
    }
    catch(err){
        //when any of the above fns in try are not resolved, this section is invoked
        console.log(err);
    }
}

The async and await keywords are usually used in combination with each other.

First, the async keyword is prepended to a function definition:

async function functionName() {

}

Then, the await keyword can be prepended to any statement within the async function; await functionName. It is used to schedule execution of the rest of the code once functionName() is resolved/ finished.

In conclusion, when used together, async/await make asynchronous non-blocking code read like simpler synchronous blocking code, hence providing an elegant, much easier to understand code style alternative to promises and callbacks.

Any thoughts and comments, would love to hear those!

P.S. I've created a Github repository here, with a simple practical illustration of the three asynchronous JS concepts (callbacks, promises, async/await).