Skip to content

async Functions and await

Promise chains work, but they require you to break your logic into a series of callbacks. async/await is a syntax built on top of Promises that lets you write the same logic as sequential, top-to-bottom code — no callbacks required.

Mark a function async and it always returns a Promise:

async function greet() {
return 'Hello';
}
const result = greet();
console.log(result); // Promise { 'Hello' }
result.then(value => console.log(value)); // 'Hello'

Even though greet returns a plain string, the async keyword wraps it in a fulfilled Promise automatically. This is why async functions can always be treated like Promises — they always are Promises.

Inside an async function, use await in front of any Promise to pause execution until that Promise settles, and then give you the resolved value:

async function loadRates() {
const response = await fetch('https://open.er-api.com/v6/latest/USD');
const data = await response.json();
return data.rates;
}

await fetch(...) pauses the function until the network response arrives — then response holds the actual Response object, not a Promise. await response.json() pauses again until the body is parsed — then data holds the actual parsed object.

The function is still non-blocking. While loadRates is paused at await, the rest of the browser (event listeners, animations, other code) keeps running.

The same logic, both ways:

// Promise chain
function fetchRatesChain() {
return fetch('https://open.er-api.com/v6/latest/USD')
.then(response => {
if (!response.ok) throw new Error(`API error ${response.status}`);
return response.json();
})
.then(data => data.rates);
}
// async/await
async function fetchRatesAsync() {
const response = await fetch('https://open.er-api.com/v6/latest/USD');
if (!response.ok) throw new Error(`API error ${response.status}`);
const data = await response.json();
return data.rates;
}

Same behavior. The async/await version looks like synchronous code — each step is a plain assignment.

await can only be used inside async functions

Section titled “await can only be used inside async functions”

You will get a syntax error if you use await outside an async function:

// ✗ SyntaxError — await outside async function
const rates = await fetchRates();
// ✓ Inside an async function
async function init() {
const rates = await fetchRates();
renderExpenses(expenses, rates);
}
init();

At the top level in a module (type="module"), await is allowed directly — this is called top-level await and is supported in modern browsers.

async works with arrow functions too:

const fetchRates = async () => {
const response = await fetch('https://open.er-api.com/v6/latest/USD');
const data = await response.json();
return data.rates;
};
  1. Write an async function getRate(currency) that fetches https://open.er-api.com/v6/latest/USD, parses the response, and returns data.rates[currency]. Call it with 'EUR' and log the result.
  2. Write an async arrow function version of the same thing.
  3. Log the return value of an async function — confirm it is a Promise. Then use .then on it to get the actual value.
  4. Call getRate('EUR') at module top level (in a type="module" script) using await directly — confirm it works without an async wrapper.
  • async function always returns a Promise. Return values are auto-wrapped.
  • await expression pauses the function until the Promise resolves and gives you the value.
  • await can only be used inside async functions (or at the module top level).
  • async/await does not change how Promises work — it is cleaner syntax for the same behavior.
  • The function is still non-blocking — await pauses the function, not the browser.