Skip to main content

The async keyword

The async keyword before a function declaration means that the function always returns a Promise, and that await may be used inside it. The returned value is automatically wrapped in Promise.resolve(value), so a call to such a function can always be continued with .then() or await.

Theory

TL;DR

  • async function myFunc() { ... } always returns a Promise, even if its body contains no promise at all.
  • A return value inside becomes Promise.resolve(value).
  • A throw error inside becomes Promise.reject(error).
  • await is allowed only inside async functions (or as top-level await in a module).
  • await suspends the function itself but does not block the thread: JS runs other tasks meanwhile.
  • Errors are conveniently caught with try...catch instead of .catch().

Quick example

javascript
async function myFunc() { return 42; } myFunc().then(console.log); // 42 // same as: function myFunc() { return Promise.resolve(42); }

What async actually adds

The async keyword before a function declaration:

javascript
async function myFunc() { ... }

means that:

  1. The function always returns a Promise.
  2. await may be used inside it.
  3. The returned value is automatically wrapped in Promise.resolve(value).

Example 1, a plain value turns into a promise

javascript
async function hello() { return 'Hello!'; } hello().then(console.log);

Output:

text
Hello!

Under the hood this is the same as:

javascript
function hello() { return Promise.resolve('Hello!'); }

Example 2, throwing an error turns into a reject

javascript
async function fail() { throw new Error('Failure!'); } fail().catch(err => console.error(err.message));

Output:

text
Failure!

If a throw happens inside an async function, the promise is automatically rejected (Promise.reject).

Example 7, an arrow function. Arrow async functions work too:

javascript
const load = async () => { const res = await fetch('/data'); return res.json(); };

How await works

await can be used only inside async functions. It suspends the function until the promise settles.

Example 3, using await

javascript
async function getData() { console.log('Loading...'); const res = await fetch('https://api.github.com/users/octocat'); const user = await res.json(); console.log('Done:', user.login); } getData();

The call getData() itself returns a promise, so it can be awaited as well:

javascript
await getData();

(only in an async context or with top-level await).

When the interpreter meets await somePromise, it:

  1. suspends execution of the current function;
  2. waits until the promise becomes fulfilled or rejected;
  3. resumes execution:
    • returning the value (resolve);
    • or throwing the error (reject).

Example 4, sequential execution

javascript
async function steps() { console.log('1'); await new Promise(res => setTimeout(res, 1000)); console.log('2'); await new Promise(res => setTimeout(res, 1000)); console.log('3'); } steps();

Output:

text
1 (after 1 second) 2 (after 1 second) 3

Thanks to await the code looks synchronous, but it is in fact non-blocking: JS keeps running other tasks in the meantime.

Sequential or parallel

If you do not await immediately, the promises start running in parallel.

Example 5, running promises in parallel

javascript
async function parallel() { const p1 = fetch('/api/user'); const p2 = fetch('/api/posts'); const [user, posts] = await Promise.all([p1, p2]); console.log('Both responses received'); }

This way you do not wait for each request separately, both run at the same time. The sequential version (two await fetch(...) in a row) takes roughly the sum of the durations, the parallel one takes the duration of the slowest request.

Error handling

In async functions errors are conveniently caught with try/catch instead of .catch().

Example 6, combining with try...catch

javascript
async function loadData() { try { const res = await fetch('/bad-url'); const data = await res.json(); console.log('OK:', data); } catch (err) { console.error('Error:', err.message); } }

try...catch catches both synchronous exceptions in the function body and rejected promises that you awaited.

Summary

Think of async as a promise given, and of await as a pause until that promise is fulfilled. That is, async makes the function "one that gives a promise", and await lets you "wait for" the result of that promise, without blocking anything else.

FeatureWhat it does
asyncmakes the function return a Promise
await"pauses" execution until the promise settles
return valueturns into Promise.resolve(value)
throw errorturns into Promise.reject(error)
Errorscan be caught with try...catch
Usageonly inside async (or top-level await)

Common mistakes

  • Thinking async makes code faster. It only changes the result type to a Promise; the computation does not become parallel.
  • await inside a loop for independent requests. Such code becomes sequential; independent operations need Promise.all.
  • Calling an async function and ignoring its promise. An error inside becomes an unhandled rejection that you never see.
  • Putting await outside an async function in a classic script: that is a SyntaxError. Top-level await works only in modules.
  • Forgetting that return await promise inside try differs from return promise. Without await the local catch will not catch the promise rejection.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.