async/await is syntax sugar introduced in ES2017 for handling asynchronous operations. It's built on top of Promises and makes asynchronous code look more like synchronous code, greatly improving code readability and maintainability.
async Functions
Basic Concept
An async function is a function declared with the async keyword. It always returns a Promise. Even if the function doesn't explicitly return a Promise, it will be wrapped in one.
Basic Usage
javascriptasync function fetchData() { return 'Hello World'; } // Equivalent to function fetchData() { return Promise.resolve('Hello World'); } fetchData().then(result => console.log(result)); // Output: Hello World
Returning a Promise
javascriptasync function fetchData() { // Return a regular value return 42; } async function fetchDataWithError() { // Throw an error throw new Error('Error occurred'); } fetchData().then(result => console.log(result)); // Output: 42 fetchDataWithError().catch(error => console.error(error.message)); // Output: Error occurred
await Expression
Basic Concept
The await keyword can only be used inside async functions. It pauses the execution of the async function, waits for the Promise to complete, and then returns the Promise's result.
Basic Usage
javascriptasync function fetchData() { const promise = Promise.resolve('Hello'); const result = await promise; console.log(result); // Output: Hello return result; } fetchData();
Waiting for Multiple Promises
javascriptasync function fetchMultipleData() { const promise1 = fetch('/api/user'); const promise2 = fetch('/api/posts'); const [userResponse, postsResponse] = await Promise.all([promise1, promise2]); const user = await userResponse.json(); const posts = await postsResponse.json(); return { user, posts }; }
Error Handling
Using try/catch
javascriptasync function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); return data; } catch (error) { console.error('Request failed:', error.message); throw error; // Can choose to rethrow error } }
Catching Specific Errors
javascriptasync function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { if (error.name === 'TypeError') { console.error('Network connection problem'); } else if (error.message.includes('HTTP error')) { console.error('Server error'); } else { console.error('Unknown error:', error); } throw error; } }
async/await vs Promise.then()
Promise.then() Chaining
javascriptfunction fetchData() { return fetch('/api/user') .then(response => response.json()) .then(user => fetch(`/api/posts/${user.id}`)) .then(response => response.json()) .then(posts => ({ user, posts })) .catch(error => { console.error(error); throw error; }); }
async/await (More Readable)
javascriptasync function fetchData() { try { const userResponse = await fetch('/api/user'); const user = await userResponse.json(); const postsResponse = await fetch(`/api/posts/${user.id}`); const posts = await postsResponse.json(); return { user, posts }; } catch (error) { console.error(error); throw error; } }
Parallel Execution
Using Promise.all
javascriptasync function fetchAllData() { const [user, posts, comments] = await Promise.all([ fetch('/api/user').then(r => r.json()), fetch('/api/posts').then(r => r.json()), fetch('/api/comments').then(r => r.json()) ]); return { user, posts, comments }; }
Using Promise.allSettled
javascriptasync function fetchAllDataWithErrors() { const results = await Promise.allSettled([ fetch('/api/user').then(r => r.json()), fetch('/api/posts').then(r => r.json()), fetch('/api/comments').then(r => r.json()) ]); results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`Request ${index} succeeded:`, result.value); } else { console.error(`Request ${index} failed:`, result.reason); } }); return results; }
Common Use Cases
1. Sequential Execution of Async Operations
javascriptasync function processItems(items) { const results = []; for (const item of items) { const result = await processItem(item); results.push(result); } return results; }
2. Parallel Execution of Async Operations
javascriptasync function processItemsParallel(items) { const promises = items.map(item => processItem(item)); const results = await Promise.all(promises); return results; }
3. Request with Timeout
javascriptasync function fetchWithTimeout(url, timeout = 5000) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId); return await response.json(); } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { throw new Error('Request timeout'); } throw error; } }
4. Retry Mechanism
javascriptasync 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 error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error(`Attempt ${i + 1} failed:`, error.message); if (i === maxRetries - 1) { throw error; } await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); } } }
Best Practices
1. Always Use try/catch
javascript// Not recommended: no error handling async function fetchData() { const response = await fetch('/api/data'); return await response.json(); } // Recommended: add error handling async function fetchData() { try { const response = await fetch('/api/data'); return await response.json(); } catch (error) { console.error('Request failed:', error); throw error; } }
2. Avoid Sequential await in Loops
javascript// Not recommended: sequential execution, slow async function processItems(items) { const results = []; for (const item of items) { const result = await processItem(item); results.push(result); } return results; } // Recommended: parallel execution, fast async function processItems(items) { const promises = items.map(item => processItem(item)); return await Promise.all(promises); }
3. Use Promise.all Appropriately
javascript// Recommended: parallel execution of independent async operations async function fetchData() { const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]); return { user, posts, comments }; }
4. Use finally for Cleanup
javascriptasync function fetchData() { let connection; try { connection = await createConnection(); const data = await connection.query('SELECT * FROM users'); return data; } catch (error) { console.error('Query failed:', error); throw error; } finally { if (connection) { await connection.close(); } } }
Common Pitfalls
1. Forgetting to Use await
javascript// Error: no await async function fetchData() { const promise = fetch('/api/data'); // promise is a Promise object, not data console.log(promise); // Output: Promise {<pending>} } // Correct: use await async function fetchData() { const response = await fetch('/api/data'); const data = await response.json(); console.log(data); }
2. Using await in Non-async Function
javascript// Error: using await in non-async function function fetchData() { const data = await fetch('/api/data'); // SyntaxError } // Correct: use await in async function async function fetchData() { const data = await fetch('/api/data'); return data; }
3. Overusing try/catch
javascript// Not recommended: overusing try/catch async function fetchData() { try { try { const response = await fetch('/api/data'); try { const data = await response.json(); return data; } catch (error) { console.error('Parse failed:', error); } } catch (error) { console.error('Request failed:', error); } } catch (error) { console.error('Unknown error:', error); } } // Recommended: use try/catch appropriately async function fetchData() { try { const response = await fetch('/api/data'); return await response.json(); } catch (error) { console.error('Request or parse failed:', error); throw error; } }
Relationship with Promise
async/await is essentially syntactic sugar for Promise, they can be converted to each other:
javascript// async/await async function fetchData() { const response = await fetch('/api/data'); return await response.json(); } // Equivalent to Promise function fetchData() { return fetch('/api/data') .then(response => response.json()); }
Summary
- async functions always return Promise: Even returning a regular value will be wrapped in a Promise
- await pauses execution: Waits for Promise to complete before continuing execution
- Use try/catch for error handling: Ensure errors are properly caught and handled
- Parallel execution improves performance: Use Promise.all to execute independent async operations in parallel
- Avoid excessive nesting: Keep code flat and clear
- Understand relationship with Promise: async/await is syntactic sugar for Promise, can be converted to each other