Asynchronous JavaScript with async/await

1. Write an Asynchronous Function with async/await

Here, we have short function that talks with a github API.

1
2
3
4
5
6
7
8
9
10
11
12
13
const fetch = require('node-fetch')
function showGithubUser(handle) {
const url = `https://api.github.com/users/${handle}`
fetch(url)
.then(resp => resp.json())
.then(user => {
console.log(user.name)
console.log(user.location)
})
}
showGithubUser('minsooshin')

It loads a specific user, and once the response comes back, it parses the body as JSON. Finally, user’s name and location are logged to the console.

This is the result of execution

Let’s see how we can convert this function into asynchronous function by using the async and await keywords.

  1. Make the function asynchronous by adding the async keyword.
  2. Using the await operator to wait until the fetch call complete.
    • The await operator takes a promise and pause the function execution until the promise is settled.
  3. Assign the return value from await operator to a variable.
  4. Take the body of the response and parse it as JSON.
  5. Print out the result.
1
2
3
4
5
6
7
8
9
10
11
const fetch = require('node-fetch')
async function showGithubUser(handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
const user = await resp.json()
console.log(user.name)
console.log(user.location)
}
showGithubUser('minsooshin')

To use this async/await, please user node 7.6 or up.

2. Call an Asynchronous Function in a Promise Chain

Now, showGithubUser function fetches the user from the github API, and then prints the name and location to the console. Let’s refactor this program such that the function only retrieves the user and returns to the caller who can decide what to do with it.

  1. Get rid of the console.log lines and just return the user; the showGithubUser is called, it returns the promise.
  2. We can build a promise chain with the .then method.
1
2
3
4
5
6
7
8
9
10
11
12
13
const fetch = require('node-fetch')
async function fetchGithubUser(handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
return await resp.json()
}
fetchGithubUser('minsooshin')
.then(user => {
console.log(user.name)
console.log(user.location)
})

This example shows that it is quite easy to call an asynchronous function as part of a promise chain. Every async function returns a promise, so you can simply call .then and .catch of the return value.

3. Convert Any Function into an Asynchronous Function

JavaScript allows us to convert any function into an async function. So far, we’ve worked function declaration, but we haven’t yet looked at function expressions, arrow functions, or methods. Let’s convert this function declaration into a function expression.

  1. Cut the name, fetchGithubUser, and introduce a variable and then assign the function expression.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const fetch = require('node-fetch')
// function expression
const fetchGithubUser = async function (handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
return await resp.json()
}
// arrow function
// const showGithubUser = async function (handle) => {
fetchGithubUser('minsooshin')
.then(user => {
console.log(user.name)
console.log(user.location)
})

Let’s look at a good use case for an asynchronous function expression. The await keyword only be used in an asynchronous functions, so we cannot use it at the top level of the file.

1
2
3
4
5
6
7
8
9
10
11
// this code doesn't work
const user = await fetchGithubUser('minsooshin')
console.log(user.name)
console.log(user.location)
// to fix, wrap with `async`
(async function() {
const user = await fetchGithubUser('minsooshin')
console.log(user.name)
console.log(user.location)
})()

Finally, let’s implement asynchronous class method.

  1. define a class with GithubApiClient name.
  2. define a method to fetch user data.
  3. put async keyword in front of the method.
  4. create an instance of the class
  5. call the method with using the instance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fetch = require('node-fetch')
class GithubApiClient {
async fetchUser (handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
return await resp.json()
}
}
(async function () {
const client = new GithubApiClient()
const user = await client.fetchUser('minsooshin')
console.log(user.name)
console.log(user.location)
})()

4. Handle Errors in Asynchronous functions

Let’s use the example from section 2. What happens if we tried to load a user that doesn’t actually exist? If you run the program you will get this.

When there is an error from promise

Let’s see what the response object looks like. We will log out the user instead of individual property.

Response object of fetchGithubUser

Our fetchGithubUser function always returns a promise which resolve this value. This is not what we want. Instead, we want to reject this promise with error message.

  1. Store the return value of the JSON to the variable
  2. If the status of the response is not successful, throw an error
  3. Add catch method to our promise chain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const fetch = require('node-fetch')
async function fetchGithubUser(handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
const body = await resp.json()
if (resp.status !== 200) {
throw Error(body.message)
}
return body
}
fetchGithubUser('minsooshin')
.then(user => {
console.log(user.name)
console.log(user.location)
})
.catch(err => {
console.log(`Error: ${err.message}`)
})

This approach works because asynchronous function will automatically return a rejected promise whenever an error is thrown. This is what we can simply write a catch method and then deal with a rejected promise.
Another advantage of async/await keywords is that we can use regular try/catch statement. This is not possible plain promises because the callback function is always invoked asynchronously.

To illustrate how async/await is combined with try/catch blocks,

  1. convert promise chain into asynchronous function
  2. Add regular try/catch statement
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const fetch = require('node-fetch')
async function fetchGithubUser (handle) {
const url = `https://api.github.com/users/${handle}`
const resp = await fetch(url)
const body = await resp.json()
if (resp.status !== 200) {
throw Error(body.message)
}
return body
}
async function showGithubUser (handle) {
try {
const user = fetchGithubUser(handle)
console.log(user.name)
console.log(user.location)
} catch (err) {
console.log(`Error: ${err.message}`)
}
}
Share Comments