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.
The async keyword
Section titled “The async keyword”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.
The await keyword
Section titled “The await keyword”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.
Comparing Promise chain to async/await
Section titled “Comparing Promise chain to async/await”The same logic, both ways:
// Promise chainfunction 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/awaitasync 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 functionconst rates = await fetchRates();
// ✓ Inside an async functionasync 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.
Arrow function syntax
Section titled “Arrow function syntax”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;};Exercise
Section titled “Exercise”- Write an
asyncfunctiongetRate(currency)that fetcheshttps://open.er-api.com/v6/latest/USD, parses the response, and returnsdata.rates[currency]. Call it with'EUR'and log the result. - Write an
asyncarrow function version of the same thing. - Log the return value of an
asyncfunction — confirm it is a Promise. Then use.thenon it to get the actual value. - Call
getRate('EUR')at module top level (in atype="module"script) usingawaitdirectly — confirm it works without anasyncwrapper.
async functionalways returns a Promise. Return values are auto-wrapped.await expressionpauses the function until the Promise resolves and gives you the value.awaitcan only be used insideasyncfunctions (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 —
awaitpauses the function, not the browser.