乐闻世界logo
搜索文章和话题

How does async/await work? What is the relationship with Promise?

2月22日 14:31

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

javascript
async 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

javascript
async 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

javascript
async function fetchData() { const promise = Promise.resolve('Hello'); const result = await promise; console.log(result); // Output: Hello return result; } fetchData();

Waiting for Multiple Promises

javascript
async 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

javascript
async 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

javascript
async 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

javascript
function 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)

javascript
async 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

javascript
async 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

javascript
async 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

javascript
async 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

javascript
async function processItemsParallel(items) { const promises = items.map(item => processItem(item)); const results = await Promise.all(promises); return results; }

3. Request with Timeout

javascript
async 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

javascript
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 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

javascript
async 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

  1. async functions always return Promise: Even returning a regular value will be wrapped in a Promise
  2. await pauses execution: Waits for Promise to complete before continuing execution
  3. Use try/catch for error handling: Ensure errors are properly caught and handled
  4. Parallel execution improves performance: Use Promise.all to execute independent async operations in parallel
  5. Avoid excessive nesting: Keep code flat and clear
  6. Understand relationship with Promise: async/await is syntactic sugar for Promise, can be converted to each other
标签:Promise