Async/Await in JavaScript #
Async/await is syntactic sugar built on top of Promises, making asynchronous code look and behave more like synchronous code.
Async Functions #
An async function always returns a Promise:
// Async function declaration
async function fetchData() {
return "data";
}
// Equivalent to:
function fetchData() {
return Promise.resolve("data");
}
// Using async functions
fetchData().then(data => {
console.log(data); // "data"
});
// Async arrow function
const getData = async () => {
return "data";
};
Await Keyword #
Await pauses execution until a Promise is resolved:
async function fetchUser(id) {
// Wait for the fetch to complete
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// Without await (for comparison)
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(user => user);
}
Error Handling #
Use try/catch for error handling with async/await:
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch data:', error);
throw error; // Re-throw if needed
}
}
// Using the function
async function main() {
try {
const data = await fetchData();
console.log('Data:', data);
} catch (error) {
console.log('Error in main:', error.message);
}
}
Multiple Async Operations #
Handle multiple async operations efficiently:
// Sequential execution (slower)
async function sequential() {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
return { user, posts, comments };
}
// Parallel execution (faster)
async function parallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchComments(1)
]);
return { user, posts, comments };
}
// Race - returns first resolved promise
async function fastest() {
const result = await Promise.race([
fetchFromServer1(),
fetchFromServer2(),
fetchFromServer3()
]);
return result;
}
// allSettled - wait for all regardless of success/failure
async function allResults() {
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(2),
fetchUser(3)
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`User ${index}:`, result.value);
} else {
console.log(`User ${index} failed:`, result.reason);
}
});
}
Async Iteration #
Process async operations in loops:
// Using for...of with await
async function processUsers(userIds) {
for (const id of userIds) {
const user = await fetchUser(id);
console.log(user);
}
}
// Using for await...of with async iterables
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
async function consume() {
for await (const value of asyncGenerator()) {
console.log(value);
}
}
// Parallel processing with map
async function processAllUsers(userIds) {
const promises = userIds.map(id => fetchUser(id));
const users = await Promise.all(promises);
return users;
}
Top-Level Await #
Use await at the top level in modules:
// In ES modules (with "type": "module" in package.json)
const response = await fetch('/api/config');
const config = await response.json();
export const API_URL = config.apiUrl;
export const TIMEOUT = config.timeout;
// Top-level await blocks module loading
// Dependents wait until this module resolves
Best Practices #
Tips for effective async/await usage:
// 1. Always return from async functions
async function good() {
const data = await fetch('/api/data');
return data; // Explicit return
}
// 2. Avoid mixing async/await with .then()
// Bad
async function mixed() {
return await fetch('/api/data').then(r => r.json());
}
// Good
async function clean() {
const response = await fetch('/api/data');
return await response.json();
}
// 3. Use Promise.all for independent operations
async function optimized() {
const [users, products] = await Promise.all([
fetchUsers(),
fetchProducts()
]);
return { users, products };
}
// 4. Create utility functions for common patterns
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
// Usage
const data = await withTimeout(fetchData(), 5000);