Demystify Promises, Async, and Await in Javascript

A Promise is a special object which was introduced by ECMAScript in 2015.

Promises work on the concept of sticking to a commitment of providing needed data whenever requested by the consumer during further program execution.
If the commitment to provide data fails it returns it status as rejected and returns fulfilled if the data is properly returned.

The promises by default are executed asynchronously and reside in memory till they are processed by the CPU. The promise is the most useful and frequently used feature within Javascript.
If you take a look at Javascript libraries like React JS, the state functions are asynchronous and use promises to update the values.

How do Promises work

While creating a promise object an executor function will be passed to the constructor as a parameter and this function has two parameters, which are also the functions and they are resolve and reject.

  • The resolve function is called it returns the data and marks the promised status as fulfilled. The data can be retrieved using then() the method.
  • The reject the function will throw an error and mark the promised status as rejected. The rejection of promises can be handled outside the promise using catch() method.

Why and When should we use Promise?

Promises are where created as a replacement for callback functions as error control needs to be separately added within the code.
unlike promises where errors can easily be handled. As the promise object is asynchronous we need to take the below-mentioned points into consideration before using promises.

  • Using Promises keeps error control easily manageable as you don’t always and everywhere use exception handling to log errors.
  • If you are aware that the function may or may behave asynchronously.
  • If you don’t want program execution to wait till the data value is computed and you want to compute it in the background and then notify the user.
  • Requesting of resources from external API. For most of the purposes, promises are used here example Fetch API, Axios, etc.
  • If the requested task is not important and maybe call later when it is evaluated.

When you must not use Promise and prefer callbacks

You see if you want to synchronously get the data then you can use await it within the async function. But the trade-off is that it makes that function asynchronous. Even though promises have largely replaced callbacks and most of the objects in Javascript use promises there are also a few good reasons I see where just using callback makes more sense.

  • Cross-Browser Compatibility: If you are building web apps that you want to run on legacy browsers such as Internet Explorer, then reconsider your choice as these browsers have not yet added support for promises.

    Note

    However, you can use Promisify Polyfill Javascript library Promisify

  • Performance: As Promises don’t execute instantaneously they occupy memory and if you are working on a complex module and you have created a lot of promises then your application performance may effect.
    However, you’ll see the major effect on performance when you are working on large projects.
  • Being Synchronous: If you want the execution of the code to only be synchronous. Then you may prefer not to use promises.
For more information check out these StackOverflow answers, When not to use promise and Why do we need a promise.

How to create a Promise Object

let simple_promise = new Promise( ( resolve, reject ) => {
    //body

    resolve( "pass data which needs to be returned" ) // marks promise as fullfilled

    reject( "pass error message or any other type" ) // marks promise as rejected and triggers catch method 
} )

Accessing Values within Promise

simple_promise.then( success_function, error_function )

Handling the rejection in Promise

The second function within .then() is the error callback function which is optional and if you don’t want to specify and still log errors then use .catch() the method at the end.

simple_promise.then( success_callback_function ).catch( e => "handle error here" )

Use Promise to return user information

let user_promise = new Promise( ( resolve, reject ) => {
    let user = {
        'name' : 'Harry',
        'designation' : 'Software Developer'
    }

    resolve( user )
} )

// accessing data returned by promise
user_promise.then( (e) => { 
                return {...e, salary : 150000 }
            } )
            .then( e => console.log(e) )
            .catch( ( e ) => console.log( e ) )

To return the values from a promise they must be passed within the resolve function only then you can access them from .then() method.
You can also evaluate those values, update them pass to the below .then() method. Similarly, you can chain multiple such .then() methods and perform different operations on them.

Using async and await with Promises

The keyword async is declared before the function. It lets that function handle promises in a synchronous way. For this an additional keyword await must also be used before promise.

Note

You can use the await keyword only if you declare the function with async.

Example of Handling Promise based asynchronous functions handle synchronously

let num1=10, num2=20;
let add_numbers = new Promise( ( resolve, reject ) => {
    resolve( num1+num2 )
} );

const display_result = async ()  => {
    let result = await add_numbers
    console.log( result)
}

display_result() //30

The await makes promise to wait till its values are computed. You’ll see the evaluated value as 30 because we have passed it within resolve the function.

Passing Extra Parameters within promise object

// using await
const add_numbers = ( num1, num2 ) => {
    return new Promise( ( resolve, reject ) => {
        resolve( num1+num2 )
    } )
}

const display_result = async ()  => {
    let result = await add_numbers( 10, 25 )
    console.log(  result) // 35
}

display_result()

The only way to pass additional parameters within promise is to use promise within function and return it.

Working with multiple promises

let eat_promise = new Promise( ( resolve, reject ) => {
    resolve( "Eating" )
} )

let sleep_promise = new Promise( ( resolve, reject ) => {
    resolve( "Sleeping" )
} )

const init = () => {
    console.log(1)
    eat_promise.then( d => console.log(d) )
    console.log(2)
    sleep_promise.then( d => console.log(d) )
    console.log(3
}

init()

/*
output
1
2
3
4
Eating
Sleeping
*/

Use await to synchronous process each promise

const init = async() => {
    console.log(1)
    await eat_promise.then( d => console.log(d) )
    console.log(2)
    await sleep_promise.then( d => console.log(d) )
    console.log(3)
}

/*
output
1
Eating
2
Sleeping
3
*/

Promise Static Methods

Promise.all( iterable )

The .all() the method will be evaluated when all the promises get fulfilled. If anyone promise gets rejected then it calls .catch() method.

let eat_promise = new Promise( ( resolve, reject ) => {
    resolve( "Eating" )
} )

let sleep_promise = new Promise( ( resolve, reject ) => {
    reject( "Sleeping" )
} )

const init = async() => {
    Promise.all( [ eat_promise, sleep_promise ] ).then( d => console.log( 'res', d ) ).catch( e => console.log( 'catch', e ) )
}

init()

/*
Output
catch Sleeping
*/

Promise.allSettled( iterable )

The .allSettled() only check if all the promises are evaluated irrespective of fulfilled or rejected.

let eat_promise = new Promise( ( resolve, reject ) => {
    resolve( "Eating" )
} )

let sleep_promise = new Promise( ( resolve, reject ) => {
    reject( "Sleeping" )
} )

const init = async() => {
    Promise.allSettled( [ eat_promise, sleep_promise ] ).then( d => console.log( 'res', d ) ).catch( e => console.log( 'catch', e ) )
}

init()

/*
Output
[
    {
        "status": "fulfilled",
        "value": "Eating"
    },
    {
        "status": "rejected",
        "reason": "Sleeping" // if failed then reaso will be shown
    }
]
*/

This returns a list with objects which has the status of the promise and also the evaluated value. If a promise is rejected then instead of the value property reason the property will be passed.

Promise.any( iterable )

The Promise executes if any one of the provided promises is fulfilled.

let eat_promise = new Promise( ( resolve, reject ) => {
    setTimeout( () => resolve( "Eating" ), 500 )
} )

let sleep_promise = new Promise( ( resolve, reject ) => {
    resolve( "Sleeping" )
} )

const init = async() => {
    Promise.any( [ eat_promise, sleep_promise ] ).then( d => console.log( 'res', d ) ).catch( e => console.log( 'catch', e ) )
}

init()

/*
Output 
res Sleeping
*/

Note

If no promise gets fulfilled then AggregateError: All promises were rejected message is displayed.

Watch Video

Final Words

Promises are the new standards for building modern applications in Javascript. It also provides a lot of benefits when used properly and all the packages and Javascript objects are using promise. If you like my post then share it with your colleagues.