Asynchronous JavaScript
How to use async/await in JavaScript
Learn how to use async/await in JavaScript, the ES2017 syntax built on promises that makes asynchronous code look synchronous and easier to read and debug.
Introduction
async and await let you write promise-based code that reads top to bottom.
An async function always returns a promise. await pauses that function until a promise settles. The rest of the program keeps running.
See the MDN async function reference for details.
Why were async/await introduced?
Promises beat deep callback nesting. Long .then() chains still get noisy.
async/await maps asynchronous steps onto familiar control flow and normal try/catch.
How it works
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
Await the promise to read its value:
const doSomething = async () => {
const result = await doSomethingAsync()
console.log(result)
}
await only works inside an async function, or at the top level of a module.
A quick example
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
const doSomething = async () => {
console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
Console output:
Before
After
I did something //after 3s
Promise all the things
Any async function returns a promise, even when you return a plain value:
const aFunction = async () => {
return 'test'
}
aFunction().then(alert) // This will alert 'test'
That is like:
const aFunction = async () => {
return Promise.resolve('test')
}
aFunction().then(alert) // This will alert 'test'
Handle errors with try and catch
A rejected awaited promise throws. Catch it with try/catch:
const loadUser = async () => {
try {
const response = await fetch('/user.json')
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
return await response.json()
} catch (error) {
console.error(error)
}
}
Uncaught errors reject the promise the async function returns.
The code is much simpler to read
Compare promise chaining for nested fetch steps:
const getFirstUserData = () => {
return fetch('/users.json') // get users list
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
return response.json()
})
.then(users => users[0]) // pick first user
.then(user => fetch(`/users/${user.name}`)) // get user data
.then(userResponse => {
if (!userResponse.ok) {
throw new Error(`HTTP error: ${userResponse.status}`)
}
return userResponse.json()
})
}
getFirstUserData()
The same flow with async/await:
const getFirstUserData = async () => {
const response = await fetch('/users.json') // get users list
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
const users = await response.json() // parse JSON
const user = users[0] // pick first user
const userResponse = await fetch(`/users/${user.name}`) // get user data
if (!userResponse.ok) {
throw new Error(`HTTP error: ${userResponse.status}`)
}
const userData = await userResponse.json() // parse JSON
return userData
}
getFirstUserData()
Multiple async functions in series
Async functions compose cleanly:
const promiseToDoSomething = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 10000)
})
}
const watchOverSomeoneDoingSomething = async () => {
const something = await promiseToDoSomething()
return something + ' and I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
const something = await watchOverSomeoneDoingSomething()
return something + ' and I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then(res => {
console.log(res)
})
Prints:
I did something and I watched and I watched as well
Run independent work concurrently
Two awaits in a row run one after the other:
const user = await loadUser()
const posts = await loadPosts()
When they do not depend on each other, start both and await Promise.all():
const [user, posts] = await Promise.all([
loadUser(),
loadPosts()
])
Easier debugging
Stack traces from async functions are often easier to follow than long promise chains. You are still async: the debugger may pause around each await.
Paste the Before/After example into a page and watch the three log lines arrive in that order.
Lesson completed