Write cleaner asynchronous code using async functions and the await keyword as syntactic sugar over Promises.
The async keyword placed before a function declaration creates an async function. Async functions always return a Promise — even if you return a plain value, it is automatically wrapped in Promise.resolve().
Function declaration:
async function fetchData() {
return 'Hello';
}
fetchData().then(value => console.log(value)); // 'Hello'Function expression:
const fetchData = async function() {
return 'Hello';
};Arrow function:
const fetchData = async () => {
return 'Hello';
};Object method:
const api = {
async getData() {
return 'data';
}
};Since async functions return Promises, you can use .then() on the result. But the real power comes from using await inside them.
The await keyword can only be used inside an async function (or at the top level of an ES module). It pauses the execution of the async function until the Promise it is waiting on settles.
async function loadUser() {
const response = await fetch('/api/user');
const user = await response.json();
console.log(user.name);
}Without await, the same code using .then() would look like this:
function loadUser() {
return fetch('/api/user')
.then(response => response.json())
.then(user => console.log(user.name));
}Key points:
await pauses only the async function — the rest of your code continues running.await throws the rejection reason as an error.await any value, not just Promises — non-Promise values are returned immediately.async function demo() {
const a = await 42; // Immediately resolves to 42
const b = await Promise.resolve('hello'); // Resolves to 'hello'
console.log(a, b); // 42 'hello'
}With Promises, you handle errors using .catch(). With async/await, you use the familiar try/catch statement instead.
async function loadData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Failed to load:', error.message);
}
}You can wrap multiple await calls in a single try block. If any of them rejects, execution jumps to the catch block:
async function processOrder() {
try {
const user = await getUser();
const order = await createOrder(user.id);
const receipt = await sendReceipt(order.id);
return receipt;
} catch (error) {
// Handles errors from ANY of the three await calls
console.error('Order failed:', error);
}
}You can also add a finally block for cleanup that runs regardless of success or failure:
async function fetchWithLoading() {
showSpinner();
try {
const data = await fetchData();
displayData(data);
} catch (error) {
showError(error);
} finally {
hideSpinner(); // Always runs
}
}The combination of async/await + try/catch is the modern standard for handling asynchronous errors in JavaScript.
Sequential vs Parallel execution:
When you await multiple Promises one after another, they run sequentially (each waits for the previous to finish):
// Sequential — takes ~2 seconds total
const user = await fetchUser(); // 1 second
const orders = await fetchOrders(); // 1 secondIf the operations are independent, use Promise.all() to run them in parallel:
// Parallel — takes ~1 second total
const [user, orders] = await Promise.all([
fetchUser(),
fetchOrders()
]);Avoid the "waterfall" anti-pattern when operations do not depend on each other.
Async IIFE — immediately invoked async function expression for quick execution:
(async () => {
const data = await fetchData();
console.log(data);
})();Top-level await — in ES modules, you can use await at the top level without wrapping in an async function:
// In an ES module (.mjs or type="module")
const data = await fetchData();
console.log(data);Top-level await is useful in module initialization code but should be used carefully since it blocks the module from finishing loading until the Promise resolves.
<div id="output"></div>
<script>
// Simulate an API call
function simulateAPI(data, delay) {
return new Promise(resolve => {
setTimeout(() => resolve(data), delay);
});
}
const output = document.getElementById('output');
output.innerHTML = '<p>Loading...</p>';
async function loadDashboard() {
try {
// Run in parallel with Promise.all
const [user, settings] = await Promise.all([
simulateAPI({ name: 'Alice' }, 300),
simulateAPI({ theme: 'dark' }, 200)
]);
output.innerHTML = `
<p>User: ${user.name}</p>
<p>Theme: ${settings.theme}</p>
<p>Dashboard loaded!</p>
`;
} catch (error) {
output.innerHTML = `<p>Error: ${error.message}</p>`;
}
}
loadDashboard();
</script>What does the await keyword do?