Understanding Async/Await in JavaScript

Jan 27, 2020

Async/await lets you write async code in a way that looks like sync code. You can use if statements, for loops, and try/catch in async functions!

Async

The async keyword marks a function as an async function. In the below example, test() is an async function.

async function test() {
  return 42;
}

You can also define an async arrow function:

const test = async () => 42;

Await

The special thing about async functions is that you can use the await keyword. If you await on a promise, the await keyword pauses execution of the surrounding async function until the promise fulfills or rejects. await also unwraps the promise: it gives you the fulfilled value of the promise.

async function test() {
  // `await` unwraps the promise's value
  const val = await Promise.resolve(42);
  val; // 42
}

test();

In the above example, the Promise.resolve() function means the promise is fulfilled immediately. In the below example, await pauses execution of test() for 100 ms: `

async function test() {
  const start = Date.now();
  await new Promise(resolve => setTimeout(resolve, 100));
  const elapsed = Date.now() - start;
  elapsed; // about 100
}

await is just a plain old JavaScript keyword. That means you can use it within if statements, for loops, and try/catch.

async function asyncEvenNumbers() {
  const nums = [];
  for (let i = 1; i <= 10; ++i) {
    if (i % 2 === 0) {
      const v = await Promise.resolve(i);
      nums.push(v);
    }
  }

  nums; // [2, 4, 6, 8, 10]
}

Return Value

Another special property of async functions is that they always return a promise. Even if you return a primitive value from an async function, JavaScript will wrap that value in a promise.

async function test() {
  return 42;
}

const p = test();
p instanceof Promise; // true
p.then(v => {
  v; // 42
});

That means can use await on an async function call:

async function test() {
  return 42;
}

async function main() {
  const val = await test();
  val; // 42
}

Error Handling

Error handling with async/await is a complex topic. But, at a high level, there are two patterns for handling errors.

When you await on a promise and that promise rejects, await throws an error that you can try/catch:

async function test() {
  try {
    await Promise.reject(new Error('Oops'));
  } catch (err) {
    err.message; // Oops
  }
}

You can also use the Promise#catch() function to unwrap the promise's error:

async function test() {
  const promise = Promise.reject(new Error('Oops'));

  // Unwrap the promise's error
  const err = await promise.catch(err => err);
  err.message; // 'Oops'
}

Async/await is the future of concurrency in JavaScript. "Mastering Async/Await" teaches you how to build frontend and backend apps using async/await in just a few hours. Get your copy!

Did you find this tutorial useful? Say thanks by starring our repo on GitHub!

More Fundamentals Tutorials