Async/await is syntax sugar over promises in JavaScript that allows writing asynchronous code as if it were synchronous. The async keyword before a function makes it asynchronous and guarantees to return a promise, while await suspends function execution until the promise resolves. This simplifies reading and writing asynchronous code, avoiding .then() chains.
Key benefits:
Async/await is a modern way to work with asynchronous code in JavaScript, introduced in ES2017. It allows writing asynchronous code that looks and behaves like synchronous code, making it more readable and maintainable.
The async and await keywords are always used together:
// Declaring an asynchronous function
async function fetchData() {
// await suspends execution until promise resolves
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// Usage
fetchData()
.then(result => console.log('Data:', result))
.catch(error => console.error('Error:', error));Async/await is especially convenient when working with APIs:
// With promises
function getUserDataWithPromises(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${userId}/posts`)
.then(response => response.json())
.then(posts => ({ user, posts }));
})
.catch(error => console.error('Error:', error));
}
// With async/await
async function getUserDataWithAsync(userId) {
try {
const userResponse = await fetch(`/api/users/${userId}`);
const user = await userResponse.json();
const postsResponse = await fetch(`/api/users/${userId}/posts`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('Error:', error);
throw error;
}
}When operations need to be executed one after another:
async function processUserData() {
try {
// Step 1: Get user data
const user = await fetchUser();
console.log('User received:', user.name);
// Step 2: Get user profile
const profile = await fetchProfile(user.id);
console.log('Profile received:', profile.email);
// Step 3: Get user preferences
const preferences = await fetchPreferences(profile.id);
console.log('Preferences received:', preferences.theme);
return { user, profile, preferences };
} catch (error) {
console.error('Error processing user data:', error);
throw error;
}
}Sometimes multiple independent operations need to be executed in parallel:
// Sequential execution (slower)
async function sequentialExecution() {
const users = await fetchUsers(); // 1 second
const posts = await fetchPosts(); // 1 second
const comments = await fetchComments(); // 1 second
// Total time: ~3 seconds
return { users, posts, comments };
}
// Parallel execution (faster)
async function parallelExecution() {
const [users, posts, comments] = await Promise.all([
fetchUsers(), // 1 second
fetchPosts(), // 1 second
fetchComments() // 1 second
]);
// Total time: ~1 second
return { users, posts, comments };
}async function handleSubmit(formData) {
try {
// Validate data
const validation = await validateForm(formData);
if (!validation.isValid) {
throw new Error('Form contains errors');
}
// Submit data
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Error submitting data');
}
const result = await response.json();
console.log('Form submitted successfully:', result);
return result;
} catch (error) {
console.error('Error submitting form:', error);
showErrorToUser(error.message);
}
}async function loadUserData(userId) {
try {
// First check cache
const cachedData = localStorage.getItem(`user_${userId}`);
if (cachedData) {
console.log('Data loaded from cache');
return JSON.parse(cachedData);
}
// If not in cache, load from API
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Error loading user data');
}
const userData = await response.json();
// Save to cache
localStorage.setItem(`user_${userId}`, JSON.stringify(userData));
return userData;
} catch (error) {
console.error('Error loading user data:', error);
throw error;
}
}async function fetchUserDataFromMultipleSources(userId) {
try {
// Load data from main API
const mainDataPromise = fetch(`/api/users/${userId}`).then(res => res.json());
// Load data from backup API
const backupDataPromise = fetch(`/backup-api/users/${userId}`).then(res => res.json());
// Wait for first successful result
const userData = await Promise.any([mainDataPromise, backupDataPromise]);
return userData;
} catch (error) {
if (error instanceof AggregateError) {
console.error('All data sources unavailable:', error.errors);
} else {
console.error('Error loading data:', error);
}
throw error;
}
}// ❌ Forgotten await
async function badExample() {
const data = fetch('/api/data'); // This is a promise, not data!
console.log(data.name); // undefined
}
// ✅ Proper await usage
async function goodExample() {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data.name); // real data
}// ❌ Improper error handling
async function badErrorHandling() {
const data = await fetch('/api/data').json(); // Error won't be caught properly
return data;
}
// ✅ Proper error handling
async function goodErrorHandling() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error loading data:', error);
throw error;
}
}// ❌ Excessive await usage
async function badChaining() {
const response = await fetch('/api/data');
const data = await response.json();
const processed = await processData(data);
const validated = await validateData(processed);
return validated;
}
// ✅ Chaining without excessive await
async function goodChaining() {
const response = await fetch('/api/data');
const data = await response.json();
return validateData(processData(data));
}| Feature | Promises | Async/Await |
|---|---|---|
| Readability | .then() chains | Linear code |
| Error Handling | .catch() | try/catch |
| Debugging | Harder due to chains | Easier, call stack preserved |
| Performance | Same | Same |
| Compatibility | All modern browsers | Requires transpilation for older browsers |
Async/await is a powerful tool that makes asynchronous code more readable and maintainable. Understanding async/await is critically important for modern JavaScript development.
What will be output to the console and why? Fix the code if necessary:
async function processData() {
console.log('1. Function start');
const data1 = await fetch('/api/data1');
console.log('2. After first request');
const data2 = fetch('/api/data2');
console.log('3. After second request');
const result1 = data1.json();
console.log('4. After parsing first response');
const result2 = await data2.json();
console.log('5. After parsing second response');
return { result1, result2 };
}
processData()
.then(results => console.log('6. Results:', results))
.catch(error => console.error('7. Error:', error.message));Answer: The code contains several errors. Correct version:
async function processData() {
console.log('1. Function start');
const response1 = await fetch('/api/data1');
console.log('2. After first request');
const response2 = await fetch('/api/data2'); // Don't wait, just start
console.log('3. After second request');
const result1 = await response1.json(); // Added await
console.log('4. After parsing first response');
const result2 = await response2.json(); // await is already here
console.log('5. After parsing second response');
return { result1, result2 };
}
processData()
.then(results => console.log('6. Results:', results))
.catch(error => console.error('7. Error:', error.message));Error explanations:
Execution order:
It’s important to understand that await suspends function execution until the promise resolves, but doesn’t block the entire execution thread.
Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪