ComparisonComparisons

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.

On this page

Side-by-side comparison

#

Example

#
JavaScript
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())
Console
130
130

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

#
Syntax
async function name() {
  const value = await promise
  return value
}

promise.then((value) => value).catch((error) => {})

More examples

#
Error handling: try...catch vs catch()
JavaScript
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()
Console
await: Session expired
then: Session expired
Pitfall: a forgotten return in then()
JavaScript
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())
Console
undefined
Lesson 1 completed

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(), remember return at every step of the chain. Without it, the next step doesn’t wait for the inner promise.
  • Inside an async function, handle errors with a try...catch block. For a plain chain, use a single catch() at its end.

Related entries

#

Spotted a mistake or missing an example? Write to us.