Using async and await in JavaScript

In my previous tutorial, we covered the basics of promises in JavaScript. I concluded the article by saying that the promises allow us to run our code asynchronously.

In this tutorial, we will learn about the async and await keywords in JavaScript which enable us to use promises effectively and write cleaner asynchronous code.

Defining async Functions

Let’s begin the discussion with asynchronous functions. Consider the following greeting function:

1
function greet() {
2
  return "Hello, World!";
3
}
4

5
// Outputs: Hello, World!

6
console.log(greet());

It is just a regular JavaScript function that we have all seen before. All it does is return a string that says “Hello, World!”. However, we can turn it into an asynchronous function by simply adding the word async before it as shown below:

1
async function greet() {
2
  return "Hello, World!";
3
}
4

5
// Outputs: Promise { <state>: "fulfilled", <value>: "Hello, World!" }

6
console.log(greet());

This time, the function returns a Promise object with its state property set to fulfilled and value set to Hello, World!. In other words, the promise was resolved successfully.

We are still returning the string “Hello, World!” inside the function definition. However, the use of async keywords means that the returned values will be wrapped into a resolved Promise object. The value of the resolved promise would be the same value that we returned from the async function.

You could also return a promise of your own form the async function as shown below:

1
async function greet() {
2
  return Promise.resolve("Hello, World!");
3
}
4

5
// Outputs: Hello, World!

6
greet().then((value) => console.log(value));

Basically, the async keyword helps us define functions that will always return a promise. You can either return a promise explicitly yourself or let the function wrap any returned value that is not a promise into a promise.

The await Keyword

Any async function can contain zero or more await expressions. It is important to remember that the await keyword is only valid inside async functions. The await keyword is used to wait for a Promise to resolve or reject and then get the fulfilled value.

We use the await keyword with the following syntax:

1
await expression

The expression can be a native Promise in which case it would be used directly and waited for natively. There would be no implicit calls to then() in this case. The expression can be a thenable object in which case a new Promise will be constructed by calling the then() method. The expression can also be a non-thenable value. In this case, an already fulfilled Promise will be constructed for our use.

Let’s say a promise is already fulfilled. The execution of the async function will still be paused until the next tick. This is important to keep in mind.

Here is an example of using the await keyword within an async function:

1
async function greet() {
2
  let greeting = await "Hello, World!";
3
  return greeting;
4
}
5

6
// Outputs: [Function: Promise]

7
console.log(greet().constructor);
8

9
// Outputs: Hello, World!

10
greet().then((msg) => console.log(msg));

Here is another example of using the await keyword with an async function while explicitly using a promise:

1
async function greet() {
2
  let greeting = new Promise((resolve) => {
3
    setTimeout(() => {
4
      resolve("Hello, World!");
5
    }, 2000);
6
  });
7
  
8
  return greeting;
9
}
10

11
// Outputs: [Function: Promise]

12
console.log(greet().constructor);
13

14
// Outputs: Hello, World!

15
greet().then((msg) => console.log(msg));

This time, we have explicitly used a Promise that resolves in 2 seconds. Therefore, the “Hello, World” greeting gets printed after two seconds.

Understanding the Execution Order of Statements

We will now write two different greeting functions and see the order in which they output the results.

1
function n_greet(person) {
2
  return `Hello, ${person}!`;
3
}
4

5
async function a_greet(person) {
6
  let greeting = await `Hello, ${person}!`;
7
  return greeting;
8
}

Our first function n_greet() is a normal function that returns a string as output. Our second function is an async function which uses an expression after the await keyword. The return value in this case is an already-fulfilled promise.

Here is the code snippet which calls all these functions and logs the output:

1
a_greet("Andrew").then((msg) => console.log(msg));
2
console.log(n_greet("Adam"));
3

4
/* Output in order:

5
Hello, Adam!

6
Hello, Andrew! */

The n_greet() function call that greeted Adam was at the end. However, he is greeted first in the output. This is because the function call directly returns a string.

The a_greet() function call that greeted Andrew was at the beginning and it resulted in the construction of an already fulfilled promise. However, the execution was still paused until the next tick. That’s why the output greeting occurs after the greeting to Adam.

Now, we will define a bit more complicated async function with multiple statements. One of those statements will have the await keyword. You will see that the use of await keyword inside an async function pauses the execution of other statements that come after the await statement.

1
function timeout(ms) {
2
    return new Promise(resolve => setTimeout(resolve, ms))    
3
}
4

5
async function aa_greet(person) {
6
  console.log("Before Await..."); 
7
  await timeout(2000);
8
  let greeting = `Hello, ${person}!`;
9
  console.log("After Await...");
10
  
11
  return greeting;
12
}
13

Our async function here contains an explicitly defined promise preceded by the await keyword. This means that await keyword will wait for the promise to be fulfilled and then return the fulfilled value. The promise will take 2 seconds to fulfill so we should see “After Await…” in our console log after around 2 seconds.

Here is the code snippet which will log some statements around our async function:

1
console.log("Before Greeting Function...");
2
aa_greet("Monty").then((msg) => console.log(msg));
3
console.log("After Greeting Function...");
4

5
/* Output in Order

6
23:42:15.327 Before Greeting Function...

7
23:42:15.331 Before Await...

8
23:42:15.331 After Greeting Function...

9
23:42:17.333 After Await...

10
23:42:17.333 Hello, Monty! */

The string “Before Greeting Function…” gets logged first because this is the first call we make. After that, we call our aa_greet() function. This results in output of the string “Before Await…”. Then, the browser encounters the await keyword. So it waits for the promise to resolve. In the mean time, the execution of code outside the aa_greet() function continues. This is why we get the “After Greeting Function…” string as output in the next log entry.

Once the promise is resolved, the browser continues execution and we get “After Await…” as output. Finally, our resolved greeting is returned as a promise because we are using an async function. We call the then() method on this resolved promise and log “Hello, Monty!” to the console.

Using async and await With the Fetch API

A common use-case for the await keyword is to fetch data from a remote API. This allows much cleaner code than nested callbacks or promise chaining.

1
async function getData() {
2
    // use the fetch API to fetch data from an API endpoint

3
    const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
4
    
5
    // check if the response is okay (HTTP status code 200-299)

6
    if (!response.ok) {
7
      throw new Error(`HTTP error! Status: ${response.status}`);
8
    }
9
    
10
    // parse the response as JSON

11
    const data = await response.json();
12
    
13
    return data;
14
}

In this function, first we await the initial response to an API query. If the response is okay, we then await the completed response data as JSON. We return the JSON data, but remember that since this is an async function, we’re actually returning a promise that eventually resolves to that data. So, if you want to access the resulting data you’ll have to use something like the await keyword again!

1
const data = await getData();

Final Thoughts

After learning about the Promise object in previous tutorial, we discussed async functions and the await keyword in this tutorial. You should now be able to write your own async functions that use the await keyword to implement promise-based behavior with a cleaner and more readable code.


This content originally appeared on Envato Tuts+ Tutorials and was authored by Monty Shokeen

In my previous tutorial, we covered the basics of promises in JavaScript. I concluded the article by saying that the promises allow us to run our code asynchronously.

In this tutorial, we will learn about the async and await keywords in JavaScript which enable us to use promises effectively and write cleaner asynchronous code.

Defining async Functions

Let's begin the discussion with asynchronous functions. Consider the following greeting function:

1
function greet() {
2
  return "Hello, World!";
3
}
4
5
// Outputs: Hello, World!

6
console.log(greet());

It is just a regular JavaScript function that we have all seen before. All it does is return a string that says "Hello, World!". However, we can turn it into an asynchronous function by simply adding the word async before it as shown below:

1
async function greet() {
2
  return "Hello, World!";
3
}
4
5
// Outputs: Promise { <state>: "fulfilled", <value>: "Hello, World!" }

6
console.log(greet());

This time, the function returns a Promise object with its state property set to fulfilled and value set to Hello, World!. In other words, the promise was resolved successfully.

We are still returning the string "Hello, World!" inside the function definition. However, the use of async keywords means that the returned values will be wrapped into a resolved Promise object. The value of the resolved promise would be the same value that we returned from the async function.

You could also return a promise of your own form the async function as shown below:

1
async function greet() {
2
  return Promise.resolve("Hello, World!");
3
}
4
5
// Outputs: Hello, World!

6
greet().then((value) => console.log(value));

Basically, the async keyword helps us define functions that will always return a promise. You can either return a promise explicitly yourself or let the function wrap any returned value that is not a promise into a promise.

The await Keyword

Any async function can contain zero or more await expressions. It is important to remember that the await keyword is only valid inside async functions. The await keyword is used to wait for a Promise to resolve or reject and then get the fulfilled value.

We use the await keyword with the following syntax:

1
await expression

The expression can be a native Promise in which case it would be used directly and waited for natively. There would be no implicit calls to then() in this case. The expression can be a thenable object in which case a new Promise will be constructed by calling the then() method. The expression can also be a non-thenable value. In this case, an already fulfilled Promise will be constructed for our use.

Let's say a promise is already fulfilled. The execution of the async function will still be paused until the next tick. This is important to keep in mind.

Here is an example of using the await keyword within an async function:

1
async function greet() {
2
  let greeting = await "Hello, World!";
3
  return greeting;
4
}
5
6
// Outputs: [Function: Promise]

7
console.log(greet().constructor);
8
9
// Outputs: Hello, World!

10
greet().then((msg) => console.log(msg));

Here is another example of using the await keyword with an async function while explicitly using a promise:

1
async function greet() {
2
  let greeting = new Promise((resolve) => {
3
    setTimeout(() => {
4
      resolve("Hello, World!");
5
    }, 2000);
6
  });
7
  
8
  return greeting;
9
}
10
11
// Outputs: [Function: Promise]

12
console.log(greet().constructor);
13
14
// Outputs: Hello, World!

15
greet().then((msg) => console.log(msg));

This time, we have explicitly used a Promise that resolves in 2 seconds. Therefore, the "Hello, World" greeting gets printed after two seconds.

Understanding the Execution Order of Statements

We will now write two different greeting functions and see the order in which they output the results.

1
function n_greet(person) {
2
  return `Hello, ${person}!`;
3
}
4
5
async function a_greet(person) {
6
  let greeting = await `Hello, ${person}!`;
7
  return greeting;
8
}

Our first function n_greet() is a normal function that returns a string as output. Our second function is an async function which uses an expression after the await keyword. The return value in this case is an already-fulfilled promise.

Here is the code snippet which calls all these functions and logs the output:

1
a_greet("Andrew").then((msg) => console.log(msg));
2
console.log(n_greet("Adam"));
3
4
/* Output in order:

5
Hello, Adam!

6
Hello, Andrew! */

The n_greet() function call that greeted Adam was at the end. However, he is greeted first in the output. This is because the function call directly returns a string.

The a_greet() function call that greeted Andrew was at the beginning and it resulted in the construction of an already fulfilled promise. However, the execution was still paused until the next tick. That's why the output greeting occurs after the greeting to Adam.

Now, we will define a bit more complicated async function with multiple statements. One of those statements will have the await keyword. You will see that the use of await keyword inside an async function pauses the execution of other statements that come after the await statement.

1
function timeout(ms) {
2
    return new Promise(resolve => setTimeout(resolve, ms))    
3
}
4
5
async function aa_greet(person) {
6
  console.log("Before Await..."); 
7
  await timeout(2000);
8
  let greeting = `Hello, ${person}!`;
9
  console.log("After Await...");
10
  
11
  return greeting;
12
}
13

Our async function here contains an explicitly defined promise preceded by the await keyword. This means that await keyword will wait for the promise to be fulfilled and then return the fulfilled value. The promise will take 2 seconds to fulfill so we should see "After Await..." in our console log after around 2 seconds.

Here is the code snippet which will log some statements around our async function:

1
console.log("Before Greeting Function...");
2
aa_greet("Monty").then((msg) => console.log(msg));
3
console.log("After Greeting Function...");
4
5
/* Output in Order

6
23:42:15.327 Before Greeting Function...

7
23:42:15.331 Before Await...

8
23:42:15.331 After Greeting Function...

9
23:42:17.333 After Await...

10
23:42:17.333 Hello, Monty! */

The string "Before Greeting Function..." gets logged first because this is the first call we make. After that, we call our aa_greet() function. This results in output of the string "Before Await...". Then, the browser encounters the await keyword. So it waits for the promise to resolve. In the mean time, the execution of code outside the aa_greet() function continues. This is why we get the "After Greeting Function..." string as output in the next log entry.

Once the promise is resolved, the browser continues execution and we get "After Await..." as output. Finally, our resolved greeting is returned as a promise because we are using an async function. We call the then() method on this resolved promise and log "Hello, Monty!" to the console.

Using async and await With the Fetch API

A common use-case for the await keyword is to fetch data from a remote API. This allows much cleaner code than nested callbacks or promise chaining.

1
async function getData() {
2
    // use the fetch API to fetch data from an API endpoint

3
    const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
4
    
5
    // check if the response is okay (HTTP status code 200-299)

6
    if (!response.ok) {
7
      throw new Error(`HTTP error! Status: ${response.status}`);
8
    }
9
    
10
    // parse the response as JSON

11
    const data = await response.json();
12
    
13
    return data;
14
}

In this function, first we await the initial response to an API query. If the response is okay, we then await the completed response data as JSON. We return the JSON data, but remember that since this is an async function, we're actually returning a promise that eventually resolves to that data. So, if you want to access the resulting data you'll have to use something like the await keyword again!

1
const data = await getData();

Final Thoughts

After learning about the Promise object in previous tutorial, we discussed async functions and the await keyword in this tutorial. You should now be able to write your own async functions that use the await keyword to implement promise-based behavior with a cleaner and more readable code.


This content originally appeared on Envato Tuts+ Tutorials and was authored by Monty Shokeen


Print Share Comment Cite Upload Translate Updates
APA

Monty Shokeen | Sciencx (2023-03-21T00:19:52+00:00) Using async and await in JavaScript. Retrieved from https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/

MLA
" » Using async and await in JavaScript." Monty Shokeen | Sciencx - Tuesday March 21, 2023, https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/
HARVARD
Monty Shokeen | Sciencx Tuesday March 21, 2023 » Using async and await in JavaScript., viewed ,<https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/>
VANCOUVER
Monty Shokeen | Sciencx - » Using async and await in JavaScript. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/
CHICAGO
" » Using async and await in JavaScript." Monty Shokeen | Sciencx - Accessed . https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/
IEEE
" » Using async and await in JavaScript." Monty Shokeen | Sciencx [Online]. Available: https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/. [Accessed: ]
rf:citation
» Using async and await in JavaScript | Monty Shokeen | Sciencx | https://www.scien.cx/2023/03/21/using-async-and-await-in-javascript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.