DEV Community

Cover image for Let's explore async, await and promise in JavaScript
Dhanusha Perera
Dhanusha Perera

Posted on

Let's explore async, await and promise in JavaScript

About This Article

in this article, we explore what async and await keywords are,
traits, and how we can use those keywords with few examples. Since we deal with async and await, we can discuss the Promises in JavaScript too.

 

What Is Async And Await in JavaScript?

async and await function expression sample image

async and await keywords are added to the JavaScript language more recently (It was added as a part of the ECMAScript 2017 JavaScript edition).

Both async and await are keywords in JavaScript language. async keyword is used in front of a function (before a function definition).
Then, inside function(within that function) you can use await keyword.

The keyword await is used with a promise and within async function. What it does is, it makes JavaScript wait until the promise settles and return its result.

If this is not clear for you, do not worry. Before we dig into async and await. Let's understand synchronous vs asynchronous execution nature in JavaScript. Let's go step by step.

 

Synchronous vs Asynchronous Execution

Why do we need async and await, and promises? That is good question.

JavaScript is a single threaded programming language. Did you know that the ECMAScript engine is responsible to run the JavaScript source code?

Yes, the ECMAScript engine is responsible to execute your JavaScript code. Your favorite browser also consists of an ECMAScript engine. Did you know that?

Okay, let’s quickly find out what ECMAScript engines are used in popular web browsers.
 

Web Browser ECMAScript engines
Mozilla Firefox SpiderMonkey
Google Chrome V8
Opera Carakan
Safari JavaScriptCore
Edge Chakra

Javascript code runs in a single thread, which means the ECMAScript engine is handling things using a single thread.

We write more and more code as programmers, but there is a single thread to run these all instructions. Some instructions that we give are executed in sequence. That means one after another.

Execution order in JavaScript is traditionally top to bottom. This is called 'synchronous execution'.

Notice this example given below. Execution happens one after another.

console.log('Hello Detective!'); // first, this is executed
console.log('I am Lucifer Morningstar!'); // after the first line, this line is executed
console.log('Welcome to Lux.'); // this line executed thirdly.
Enter fullscreen mode Exit fullscreen mode

 
Programmers face some issues(or challenges) when only working with synchronous code because each statement waits for the previous statement to finish before executing.

Think about a single-threaded language like JavaScript, executes the code just only synchronously, If so; think about a scenario that there is a long-running, or CPU-intensive task, or I/O operation is going on, the program may freeze until that particular task is done. This is where asynchronous programming comes into play and makes it possible to express waiting for long-running actions without freezing the program during these actions.

That means when execution happens JavaScript does not wait for, some tasks or instructions that takes long time to run and blocks the synchronous code. That way it allows running rest of the code.

Asynchronous code does not have to wait, your program can continue to run. JavaScript's environment implements this style of programming using callbacks and functions that are called when the actions complete.

Let's understand what promises are.

 

Promises in JavaScript

A promise in JavaScript is something like a real-world promise that we make. If someone promised to do something, basically, there can be two outcomes.

if he or she can achieve the task successfully, outcome will be returned. Otherwise, if he or she could not achieve the task successfully, reason (why it failed) will be returned.

In fact, a promise is an object representing the eventual completion or
failure of an asynchronous operation.

Promise is a competitively new feature of JavaScript language, and it is used to handle an asynchronous result of an operation. We can defer the execution of a code block until an async request is completed. Thus, other operations can be run without interruption.

Promise has three states:

Image of a three states of Promise

  1. Pending: initial state, neither fulfilled nor rejected.
  2. Fulfilled (Resolve): meaning that the operation was completed successfully.
  3. Rejected: meaning that the operation failed.

We can create a promise object like this;

const myPromise = new Promise(function(resolve, reject) {
    // we can code the logic here
    // if success, we can resolve the promise
    // if fail/error, we can reject the promise
});
Enter fullscreen mode Exit fullscreen mode

Notice that we have passed a function with two parameters to the Promise constructor. It takes two parameters, one for success (resolve) and one for fail (reject).

Let’s look at an example.

function checkAge(age) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (typeof age != "number") {
                reject(new Error('Given input for age is invalid. Input should be a number.')); // reject is for fail
            } else if (age >= 18) {
                resolve('allow membership'); // resolve is for success
            } else {
                reject(new Error('disapprove the membership request')); // reject is for fail
            }
        }, 3000);
    });
}

function validateAge() {
    const result = checkAge(25);

    result.then((value) => { 
        // if promise was successful, that means we can use then() block to get the returned value
        console.log(value); // expected output: allow membership
    }).catch((reason => {
        // if promise was failed, that means we can use catch() block to get the returned value(reason - why it failed)
        console.error(reason); // catch() block will not be executed because age = 25
    }));
}

validateAge();

Enter fullscreen mode Exit fullscreen mode

If resolve executed, that means then() block will be executed. Thus, we can access the values of resolve in then() block.

If reject executed, that means catch() block will be executed. Thus, we can access the reason/error inside the catch() block.

Now, we have an idea about Promises too.

 

Async Keyword

Let’s try this code in your web browsers JavaScript console;

function hello() { return "Hello" };
hello(); //expected output: just return "Hello"
Enter fullscreen mode Exit fullscreen mode

expected output: just return "Hello"

As result, this function returned “Hello”, just nothing special right?

Alright, now, let’s look at the syntax of the async function expression, try to use the async keyword, and run again those code lines;

Syntax (async)

async function name {
statements
}

async function hello() { return "Hello" };
hello(); // expected output: it returns a promise
Enter fullscreen mode Exit fullscreen mode

expected output: it returns a promise

Now, you should notice that this function returns a promise. Notice that an async function always returns a promise even though we do not return a Promise explicitly. This is one of the traits of an async function, their return values are guaranteed to be converted to promises.

This how you can use async keyword with arrow functions;

let hello = async () => { return "Hello" };
hello(); // returns a promise

Enter fullscreen mode Exit fullscreen mode

To consume the return value, you can use .then() block.

With Regular Function.

hello().then(function (value){
    console.log(value);
});

Enter fullscreen mode Exit fullscreen mode

Using An Arrow Function

hello().then((value) => console.log(value));

// or

hello().then(console.log);
Enter fullscreen mode Exit fullscreen mode

 

Await Keyword

The await operator is used to wait for a Promise. It can only be used inside an async function.

Syntax (await)

[rv] = await expression;

expression : A Promise or any value to wait for.

rv : Returns the fulfilled value of the promise, or the value itself if it's not a Promise.

await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected).

Let’s look at examples and try to understand. We use this checkAge() method for our examples.

function checkAge(age) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (typeof age != "number") {
                reject(new Error('Given input for age is invalid. Input should be a number.')); //reject is for fail
            } else if (age >= 18) {
                resolve('allow membership'); // resolve is for success
            } else {
                reject(new Error('disapprove the membership request')); //reject is for fail
            }
        }, 3000); // approximately 3000 milliseconds = 3 seconds takes for this process
    });
}

Enter fullscreen mode Exit fullscreen mode

 
then() and catch() blocks are used in this example. Since age(input) is 25, then() block is executed.

function validateAge() {
    const result = checkAge(25);
    console.log(result); // expected output: Promise { <pending> }

    result.then((value) => {
        console.log(value); // expected output( approximately after 3 seconds): allow membership
    }).catch((reason => {
        console.error(reason); // expected output: since age = 25, catch block will not be executed
    }))
}

validateAge();
Enter fullscreen mode Exit fullscreen mode

 
Now, let’s change the age(input value) to 12, since the age = 12, catch() block is executed in this example.

function validateAge() {
    const result = checkAge(12);
    console.log(result); // expected output: Promise { <pending> }

    result.then((value) => {
        console.log(value); // since age = 12, then block will not executed
    }).catch((reason => {
        console.error(reason); // expected output: Error: disapprove the membership request
    }))
}

validateAge();
Enter fullscreen mode Exit fullscreen mode

 
Now, let’s try await keyword with this example. try and catch block used to catch the error (If reject is executed). since age is 25, catch block will not be executed in this example.

async function validateAge() {
    // try and catch block used to catch the error, if reject is executed.
    // since age is 25, catch block will not be executed in this example
    try {
        const result = await checkAge(25);
        console.log(result); // since age is 25, expected output: allow membership
    } catch (error) {
        console.error(error);
    }

}

validateAge();

Enter fullscreen mode Exit fullscreen mode

 
Let’s try the example with age = 12. since age is 12,
catch block will be executed in this example.

async function validateAge() {
    // try and catch block used to catch the error, if reject is executed.
    // since age is 12, catch block will be executed in this example
    try {
        const result = await checkAge(12);
        console.log(result);
    } catch (error) {
        console.error(error); //expected output: Error: disapprove the membership request
    }

}

validateAge();

Enter fullscreen mode Exit fullscreen mode

If the Promise is resolved, the await expression will return the resolve value.
If the Promise is rejected, the await expression throws the rejected value.

 

You Should Notice

  • An async function is a function declared with the async keyword, and the await keyword is permitted within them. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

  • If a Promise is passed to an await expression, it waits for the Promise to be fulfilled and returns the fulfilled value.

Top comments (0)