async/await vs then()
async/await lets you write asynchronous code like ordinary, sequential code, with errors in try...catch, while then() builds a chain of calls on the promise itself.
Side-by-side comparison
#How you write the successive steps
- async function
- Like ordinary code, line by line with
await - Promise.prototype.then()
- As a chain of
.then()calls
Error handling
- async function
- A
try...catchblock - Promise.prototype.then()
- A
.catch()method at the end of the chain
What calling the function returns
- async function
- Always a
Promise, even for a plainreturn - Promise.prototype.then()
- Always a new
Promise, a different one at every.then()
The most common mistake
- async function
- A forgotten
awaitbefore the call - Promise.prototype.then()
- A forgotten
returnin the middle of the chain
Parallel operations
- async function
await Promise.all([...])- Promise.prototype.then()
Promise.all([...]).then(...)
Typical use
- async function
- Many steps, conditions, loops with waiting
- Promise.prototype.then()
- A single step or a short chain without
async
Example
#const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms))
async function loadWithAwait() {
const points = await wait(100, 120)
return points + 10
}
function loadWithThen() {
return wait(100, 120).then((points) => points + 10)
}
console.log(await loadWithAwait())
console.log(await loadWithThen())Definition and usage
#These are two ways of working with the same promise mechanism, not two different technologies. An async function always returns a Promise, just as then() always does too. Inside an async function, the await operator pauses execution until the promise settles, while then() registers a function that gets called with its result.
The difference is in the notation and in error handling. With await, the code looks like ordinary, sequential statements, and you catch errors with the familiar try...catch block. then() needs a chain of calls, where every step must return a value or a promise, and errors are handled by a catch() at the end. A forgotten return in the middle of a then() chain is a common bug: the next step gets undefined and doesn’t wait for the previous result.
For many steps, conditions, and loops with waiting, use async/await, because that kind of code is easier to read and debug. A short, one-off promise, or a helper function meant to stay a regular (non-async) function, can stick with then() and catch().
Syntax
#async function name() {
const value = await promise
return value
}
promise.then((value) => value).catch((error) => {})More examples
#const fail = (ms, message) =>
new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms))
async function withAwait() {
try {
await fail(100, 'Session expired')
} catch (error) {
console.log('await:', error.message)
}
}
function withThen() {
return fail(100, 'Session expired').catch((error) => console.log('then:', error.message))
}
await withAwait()
await withThen()const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms))
function chainWithBug() {
return wait(100, 'Lesson 1').then((lesson) => {
wait(100, lesson + ' completed')
})
}
async function sequenceFixed() {
const lesson = await wait(100, 'Lesson 1')
return await wait(100, lesson + ' completed')
}
console.log(await chainWithBug())
console.log(await sequenceFixed())In chainWithBug() the function inside then() has no return, so the chain doesn’t wait for the inner promise and gets undefined. With await this kind of bug has no chance to happen, because every step always waits for the previous one.
Good practices
#- In new code with many steps, reach for
async/await. It reads like ordinary code running from top to bottom. - When writing
.then(), rememberreturnat every step of the chain. Without it, the next step doesn’t wait for the inner promise. - Inside an
asyncfunction, handle errors with atry...catchblock. For a plain chain, use a singlecatch()at its end.
Related entries
#- async functionDeclares an asynchronous function that always returns a promise and can use the await keyword inside it.
- Promise.prototype.then()Registers a function to run when the promise is fulfilled, and returns a new promise so you can chain steps together.
- awaitPauses an async function until a promise settles, then returns its value or throws its error.
- Promise.prototype.catch()Registers an error-handling function that runs when the promise is rejected.
Spotted a mistake or missing an example? Write to us.