Async/Await: Modern Asynchronous Programming
Async/await provides a cleaner syntax for working with Promises, making asynchronous code look synchronous.
What is Async/Await?
Async/await is syntactic sugar built on top of Promises:
// Promise-based
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(response => response.json())
.then(user => user);
}
// Async/await
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
Async Functions
An async function always returns a Promise:
async function greet(name) {
return `Hello, ${name}!`;
}
greet("Alice").then(message => console.log(message)); // "Hello, Alice!"
Implicit Promise Wrapping
async function test() {
return 42;
}
// Equivalent to
function test() {
return Promise.resolve(42);
}
test().then(value => console.log(value)); // 42
Await Expression
The await keyword pauses execution until a Promise settles:
async function example() {
console.log("Start");
const result = await Promise.resolve("Done");
console.log(result); // "Done"
console.log("End");
}
example();
// Output:
// Start
// Done
// End
Await with Delays
async function delayedGreeting() {
console.log("Waiting...");
await new Promise(resolve => setTimeout(resolve, 2000));
console.log("Hello after 2 seconds!");
}
delayedGreeting();
Error Handling
Try-Catch
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("Error fetching user:", error);
throw error;
}
}
fetchUser(1)
.then(user => console.log(user))
.catch(error => console.error(error));
Multiple Try-Catch Blocks
async function processData() {
try {
const data = await fetchData();
console.log("Data fetched");
} catch (error) {
console.error("Fetch error:", error);
}
try {
const processed = await processData(data);
console.log("Data processed");
} catch (error) {
console.error("Processing error:", error);
}
}
Finally Block
async function operation() {
try {
const result = await someAsyncOperation();
return result;
} catch (error) {
console.error(error);
} finally {
console.log("Cleanup"); // Always runs
}
}
Sequential vs Parallel Execution
Sequential (Slower)
async function sequential() {
const user = await fetchUser(1);
const orders = await fetchOrders(user.id);
const details = await fetchOrderDetails(orders[0].id);
return details;
}
// Takes: time1 + time2 + time3
Parallel (Faster)
async function parallel() {
const [user, posts, comments] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchComments(1)
]);
return { user, posts, comments };
}
// Takes: max(time1, time2, time3)
Practical Examples
Fetch with Retry
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`Retry ${i + 1}...`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
fetchWithRetry("https://api.example.com/data")
.then(data => console.log(data))
.catch(error => console.error(error));
Timeout
async function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms)
);
return Promise.race([promise, timeout]);
}
async function fetchWithTimeout() {
try {
const data = await withTimeout(
fetch("https://api.example.com/data").then(r => r.json()),
5000
);
console.log(data);
} catch (error) {
console.error(error);
}
}
Sequential Processing
async function processItems(items) {
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
return results;
}
async function processItem(item) {
return new Promise(resolve => {
setTimeout(() => resolve(`Processed: ${item}`), 1000);
});
}
processItems(["A", "B", "C"])
.then(results => console.log(results));
Parallel Processing
async function processItemsParallel(items) {
const promises = items.map(item => processItem(item));
return Promise.all(promises);
}
processItemsParallel(["A", "B", "C"])
.then(results => console.log(results));
Data Transformation Pipeline
async function getUserWithDetails(userId) {
try {
// Fetch user
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
// Fetch user's posts
const postsResponse = await fetch(`/api/users/${userId}/posts`);
const posts = await postsResponse.json();
// Fetch user's comments
const commentsResponse = await fetch(`/api/users/${userId}/comments`);
const comments = await commentsResponse.json();
return {
user,
posts,
comments,
postCount: posts.length,
commentCount: comments.length
};
} catch (error) {
console.error("Error fetching user details:", error);
throw error;
}
}
getUserWithDetails(1)
.then(details => console.log(details))
.catch(error => console.error(error));
Event Handler with Async
const button = document.getElementById("myButton");
button.addEventListener("click", async (event) => {
try {
button.disabled = true;
button.textContent = "Loading...";
const data = await fetch("/api/data").then(r => r.json());
console.log(data);
button.textContent = "Success!";
} catch (error) {
console.error(error);
button.textContent = "Error";
} finally {
button.disabled = false;
}
});
Async Arrow Functions
// Regular async function
async function fetchData() {
return await fetch("/api/data").then(r => r.json());
}
// Async arrow function
const fetchData = async () => {
return await fetch("/api/data").then(r => r.json());
};
// Concise
const fetchData = async () => fetch("/api/data").then(r => r.json());
Async Generators
async function* asyncGenerator() {
yield 1;
await new Promise(resolve => setTimeout(resolve, 1000));
yield 2;
await new Promise(resolve => setTimeout(resolve, 1000));
yield 3;
}
(async () => {
for await (const value of asyncGenerator()) {
console.log(value);
}
})();
Common Patterns
Debounce with Async
function debounceAsync(fn, delay) {
let timeoutId;
return async (...args) => {
clearTimeout(timeoutId);
return new Promise(resolve => {
timeoutId = setTimeout(() => {
resolve(fn(...args));
}, delay);
});
};
}
const search = debounceAsync(async (query) => {
const results = await fetch(`/api/search?q=${query}`).then(r => r.json());
return results;
}, 300);
Memoization with Async
function memoizeAsync(fn) {
const cache = new Map();
return async (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = await fn(...args);
cache.set(key, result);
return result;
};
}
const fetchUser = memoizeAsync(async (id) => {
return fetch(`/api/users/${id}`).then(r => r.json());
});
Best Practices
Use Parallel When Possible
// Bad - sequential
const user = await fetchUser(1);
const posts = await fetchPosts(1);
// Good - parallel
const [user, posts] = await Promise.all([
fetchUser(1),
fetchPosts(1)
]);
Handle Errors Properly
// Good - specific error handling
async function operation() {
try {
const result = await riskyOperation();
return result;
} catch (error) {
if (error instanceof NetworkError) {
console.error("Network error:", error);
} else if (error instanceof ValidationError) {
console.error("Validation error:", error);
} else {
console.error("Unknown error:", error);
}
throw error;
}
}
Avoid Unnecessary Await
// Bad - unnecessary await
async function bad() {
const result = await Promise.resolve(42);
return result;
}
// Good - return promise directly
async function good() {
return Promise.resolve(42);
}
Summary
- async: declares async function that returns Promise
- await: pauses execution until Promise settles
- try-catch: error handling in async functions
- Sequential: await operations one after another
- Parallel: use Promise.all() for concurrent operations
- Error handling: use try-catch-finally
- Best practice: use parallel execution when possible
Related Resources
Official Documentation
Next Steps
- Fetch API: Making HTTP Requests
- Error Handling with Promises and Async/Await
- DOM: Document Object Model Basics
Comments