async in JavaScript: writing asynchronous code that reads top to bottom

The async keyword turns a function into an asynchronous one: it always returns a promise and allows await inside its body. Here is how to use it.
3 min read
Believemy logo

Loading data, reading a file, calling an API: these operations take time, and JavaScript refuses to sit still while they finish. For years the answer was stacking callbacks, with code written in an order that no longer matched the order you read it in.

async fixed that presentation problem. The code reads again as a sequence of instructions from top to bottom, while the engine keeps working in its own way underneath.


Definition

async goes in front of a function and changes two things: the function now always returns a Promise, and the await keyword becomes usable inside its body.

JAVASCRIPT
async function loadProfile(id) {
  const response = await fetch('/api/profile/' + id);
  return response.json();
}

loadProfile(7).then(profile => console.log(profile));

The await line looks like an ordinary call, when in fact it pauses the function and hands control back to the rest of the program until the answer arrives.


What an async function always returns

This is the rule that surprises people most: whatever you write after return, calling the function produces a promise.

DeclarationWhat the call produces
function f() { return 42 }42
async function f() { return 42 }a promise fulfilled with 42
async function f() { throw ... }a rejected promise
JAVASCRIPT
async function answer() {
  return 42;
}

console.log(answer());              // Promise { 42 }
answer().then(v => console.log(v)); // 42

A value showing up as Promise in the console almost always comes from this: the function was called without await in front of it.


The four places it is written

  • In front of a declaration: async function load() {}.
  • In front of an arrow function: const load = async () => {}.
  • In front of a method, inside a class or an object: async load() {}.
  • In front of a function called immediately: (async () => { ... })(), handy for opening an asynchronous area inside a file that is not one.
Good to know

A throw inside an async function does not surface as an ordinary error: it rejects the returned promise. So you catch it with .catch(), or with a try around the call, provided that call is preceded by await.


Frequently asked questions

Question

Should every function be async?

No. An async function containing no await brings nothing: it merely wraps its result in a promise, forcing every caller to adapt. Keep the keyword for functions that genuinely wait for something.


Question

Does async slow the code down?

The keyword's own cost is negligible. What costs is how you chain the waiting: three calls awaited one after another take the sum of their durations, while starting them together with Promise.all() takes the duration of the slowest. That is where the performance lever sits, never in the keyword itself.


Question

Can I use await without async?

Yes, at the top level of a module, where await can be written directly without wrapping the code in a function. Everywhere else, in a classic script in particular, an async function is required. Our JavaScript course walks through this chain starting from promises, the step many people skip before running into these error messages.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.