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 aPromise, even if its body contains no promise at all.- A
return valueinside becomesPromise.resolve(value). - A
throw errorinside becomesPromise.reject(error). awaitis allowed only insideasyncfunctions (or as top-level await in a module).awaitsuspends the function itself but does not block the thread: JS runs other tasks meanwhile.- Errors are conveniently caught with
try...catchinstead of.catch().
Quick example
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:
async function myFunc() { ... }means that:
- The function always returns a
Promise. awaitmay be used inside it.- The returned value is automatically wrapped in
Promise.resolve(value).
Example 1, a plain value turns into a promise
async function hello() {
return 'Hello!';
}
hello().then(console.log);Output:
Hello!Under the hood this is the same as:
function hello() {
return Promise.resolve('Hello!');
}Example 2, throwing an error turns into a reject
async function fail() {
throw new Error('Failure!');
}
fail().catch(err => console.error(err.message));Output:
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:
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
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:
await getData();(only in an async context or with top-level await).
When the interpreter meets await somePromise, it:
- suspends execution of the current function;
- waits until the promise becomes
fulfilledorrejected; - resumes execution:
- returning the value (
resolve); - or throwing the error (
reject).
- returning the value (
Example 4, sequential execution
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:
1
(after 1 second)
2
(after 1 second)
3Thanks 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
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
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
asyncas a promise given, and ofawaitas a pause until that promise is fulfilled. That is,asyncmakes the function "one that gives a promise", andawaitlets you "wait for" the result of that promise, without blocking anything else.
| Feature | What it does |
|---|---|
async | makes the function return a Promise |
await | "pauses" execution until the promise settles |
return value | turns into Promise.resolve(value) |
throw error | turns into Promise.reject(error) |
| Errors | can be caught with try...catch |
| Usage | only inside async (or top-level await) |
Common mistakes
- Thinking
asyncmakes code faster. It only changes the result type to aPromise; the computation does not become parallel. awaitinside a loop for independent requests. Such code becomes sequential; independent operations needPromise.all.- Calling an
asyncfunction and ignoring its promise. An error inside becomes an unhandled rejection that you never see. - Putting
awaitoutside anasyncfunction in a classic script: that is a SyntaxError. Top-level await works only in modules. - Forgetting that
return await promiseinsidetrydiffers fromreturn promise. Withoutawaitthe localcatchwill not catch the promise rejection.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.