一、什么是 async/await?
在 Node.js 中,async/await 是一种处理异步操作的语法糖。
async/await 基于 Promise,但让异步代码看起来更像同步代码,极大地提高了代码的可读性和可维护性。
async 关键字用于声明一个函数是异步的,而 await 关键字用于等待一个 Promise 的解决(resolve)或拒绝(reject)。
使用 async/await 可以避免回调地狱(callback hell)并使错误处理更加直观。
二、基本语法
async 函数
任何函数都可以通过添加 async 关键字变成异步函数:
async function myFunction() { return "Hello World"; } // 等价于 function myFunction() { return Promise.resolve("Hello World"); }
async 函数总是返回一个 Promise。如果返回值不是 Promise,它会被自动包装成 Promise。
await 表达式
await 只能在 async 函数内部使用,它会暂停函数的执行,等待 Promise 解决,然后继续执行并返回结果:
async function fetchData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; }
三、错误处理
try/catch 方式
处理 async/await 错误最常用的方法是使用 try/catch:
async function getUser() { try { const response = await fetch('https://api.example.com/user'); const user = await response.json(); return user; } catch (error) { console.error('Error fetching user:', error); throw error; // 可以选择重新抛出错误 } }
直接处理 Promise
你也可以直接处理返回的 Promise:
getUser() .then(user => console.log(user)) .catch(error => console.error(error));
四、实际应用示例
并行执行多个异步操作
使用 Promise.all 结合 async/await 可以并行执行多个异步操作:
async function fetchMultipleUrls(urls) { try { const requests = urls.map(url => fetch(url)); const responses = await Promise.all(requests); const data = await Promise.all(responses.map(r => r.json())); return data; } catch (error) { console.error('Error fetching data:', error); throw error; } } // 使用 const data = await fetchMultipleUrls([ 'https://api.example.com/users', 'https://api.example.com/posts' ]);
顺序执行 vs 并行执行
顺序执行(较慢,适合有依赖关系的操作):
async function sequentialTasks() { const user = await getUser(); // 等待完成 const posts = await getPosts(); // 再执行下一个 return { user, posts }; }
并行执行(较快,适合独立的操作):
async function parallelTasks() { const userPromise = getUser(); const postsPromise = getPosts(); const [user, posts] = await Promise.all([userPromise, postsPromise]); return { user, posts }; }
数据库操作示例
async function getUserAndPosts(userId) { try { const user = await User.findById(userId); const posts = await Post.find({ userId }); return { user, posts }; } catch (error) { console.error('Database error:', error); throw error; } }
文件操作示例
const fs = require('fs').promises; async function readFiles() { try { const data1 = await fs.readFile('file1.txt', 'utf8'); const data2 = await fs.readFile('file2.txt', 'utf8'); console.log('Data:', data1, data2); } catch (err) { console.error('读取文件出错:', err); } }
五、最佳实践
| 实践 | 说明 |
|---|---|
| 总是处理错误 | 不要忽略 await 可能抛出的错误,使用 try/catch 或 .catch() 处理 |
| 避免不必要的 await | 如果不需要等待结果,可以直接返回 Promise |
| 合理使用并行 | 多个独立的异步操作应该并行执行(使用 Promise.all) |
| 保持代码清晰 | 避免过深的 async/await 嵌套,必要时提取函数 |
| 注意性能影响 | 每个 await 都会暂停函数执行,在循环中要特别注意 |
六、常见问题
async/await 与 Promise 的关系
async/await 是建立在 Promise 之上的语法糖。任何 async 函数都返回 Promise,任何 await 后面都可以接 Promise。
// async/await 和 Promise 可以混用 async function processUser() { const user = await getUser(); const posts = await getPosts(user.id); return posts; // 等价于 return Promise.resolve(posts) } // 返回的异步函数本身就是 Promise processUser().then(posts => console.log(posts));
为什么我的 async 函数返回 undefined?
这可能是因为忘记在 await 前使用 return,或者在 Promise 解决前函数就退出了。
// 错误示例:没有 return,返回 undefined async function getData() { await fetch('url'); // 没有 return } // 正确示例:使用 return async function getData() { return await fetch('url'); } // 或者直接返回 Promise async function getData() { return fetch('url'); // 自动包装为 Promise }
可以在顶层使用 await 吗?
在 ES 模块 中(文件以 .mjs 结尾或 package.json 中 "type": "module"),可以直接在顶层使用 await。在 CommonJS 模块中,需要包裹在 async 函数中。
// ES 模块中可以直接使用顶层 await const data = await fetchData(); console.log(data); // CommonJS 模块中需要包裹 (async () => { const data = await fetchData(); console.log(data); })();
七、本章小结
| 知识点 | 说明 |
|---|---|
| async | 声明异步函数,返回 Promise |
| await | 等待 Promise 解决,只能在 async 函数中使用 |
| 错误处理 | 使用 try/catch 或 .catch() 处理异步错误 |
| 并行执行 | 使用 Promise.all 同时执行多个独立的异步操作 |
| 顺序执行 | 使用 await 依次执行有依赖关系的异步操作 |
| 顶层 await | ES 模块中支持,CommonJS 中需包裹在 async 函数中 |
提示 :async/await 是现代 Node.js 异步编程的推荐方式。它让异步代码看起来像同步代码,大大提高了代码的可读性和可维护性。但要记住,
await会暂停函数执行,在循环中使用时要考虑性能影响,无关的操作应该用Promise.all并行执行。