How to Use Async/Await in JavaScript
Learn how to write asynchronous JavaScript code using async/await for cleaner, more readable code.
Async/await is a modern way to handle asynchronous operations in JavaScript, making code easier to read and write.
Basic Syntax
// Async function declaration
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
// Calling the async function
fetchData().then(data => console.log(data));
Why Use Async/Await?
Compare with traditional Promise chains:
// Promise chain (harder to read)
function getUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(response => response.json())
.then(posts => ({ user, posts }));
}
// Async/await (cleaner)
async function getUserData(userId) {
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
const postsResponse = await fetch(`/api/posts/${user.id}`);
const posts = await postsResponse.json();
return { user, posts };
}
Error Handling
Use try/catch for error handling:
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const user = await response.json();
return user;
} catch (error) {
console.error('Failed to fetch user:', error);
throw error; // Re-throw if you want calling code to handle it
}
}
Parallel Execution
Run multiple async operations simultaneously:
async function fetchAllData() {
// Sequential (slow) - waits for each one
const users = await fetch('/api/users').then(r => r.json());
const posts = await fetch('/api/posts').then(r => r.json());
// Parallel (fast) - runs simultaneously
const [usersData, postsData] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
return { users: usersData, posts: postsData };
}
Async Arrow Functions
// Arrow function syntax
const fetchData = async () => {
const response = await fetch('/api/data');
return response.json();
};
// In array methods
const urls = ['/api/user/1', '/api/user/2', '/api/user/3'];
// Fetch all users in parallel
const users = await Promise.all(
urls.map(async (url) => {
const response = await fetch(url);
return response.json();
})
);
Async in Loops
Be careful with async in loops:
// Sequential processing
async function processSequentially(items) {
for (const item of items) {
await processItem(item);
}
}
// Parallel processing
async function processInParallel(items) {
await Promise.all(items.map(item => processItem(item)));
}
// Note: forEach doesn't wait for async!
// This WON'T work as expected:
items.forEach(async (item) => {
await processItem(item); // Doesn't wait!
});
Using with setTimeout
Create a delay function:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
if (i < retries - 1) {
await delay(1000 * (i + 1)); // Exponential backoff
} else {
throw error;
}
}
}
}
Top-Level Await
In ES modules, you can use await at the top level:
// In an ES module (.mjs or type="module")
const response = await fetch('/api/config');
const config = await response.json();
export { config };
Summary
- Use
asynckeyword to define an async function - Use
awaitto pause execution until a Promise resolves - Wrap in try/catch for error handling
- Use
Promise.all()for parallel execution - Be careful with async in loops - prefer
for...ofoverforEach