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
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/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.