Node.js async/await 语法
核心知识点
async/await 概念
async/await 是 ES2017 引入的语法特性,它是基于 Promise 的语法糖,让异步代码的写法更接近同步代码,提高了代码的可读性和可维护性。
async 函数
async 关键字用于声明一个异步函数:
async function asyncFunction() {
// 函数体
return 'Hello, async!';
}await 表达式
await 关键字用于等待一个 Promise 完成,只能在 async 函数内部使用:
async function asyncFunction() {
const result = await promise;
return result;
}错误处理
在 async/await 中,可以使用 try/catch 语法来处理错误:
async function asyncFunction() {
try {
const result = await promise;
return result;
} catch (error) {
console.error('发生错误:', error);
// 处理错误
}
}async/await 与 Promise 的关系
async/await 是基于 Promise 的,每个 async 函数都会返回一个 Promise,await 表达式会等待这个 Promise 完成。
实用案例
案例一:基本 async/await 使用
const fs = require('fs').promises; // Node.js 10+ 提供的 Promise API
// 使用 async/await 处理文件操作
async function processFiles() {
try {
// 读取文件
const data = await fs.readFile('example.txt', 'utf8');
console.log('读取的文件内容:', data);
// 写入文件
await fs.writeFile('output.txt', data.toUpperCase());
console.log('文件写入成功');
// 读取新文件
const newData = await fs.readFile('output.txt', 'utf8');
console.log('写入后读取的内容:', newData);
return '文件处理完成';
} catch (error) {
console.error('处理文件时发生错误:', error);
throw error; // 可以选择重新抛出错误
}
}
// 调用异步函数
processFiles()
.then(result => {
console.log('最终结果:', result);
})
.catch(error => {
console.error('捕获到错误:', error);
});
// 或者在另一个 async 函数中调用
async function main() {
try {
const result = await processFiles();
console.log('从 main 函数中获取结果:', result);
} catch (error) {
console.error('main 函数中的错误:', error);
}
}
main();案例二:并行执行异步操作
const fs = require('fs').promises;
async function parallelFileOperations() {
try {
// 并行读取多个文件
const [data1, data2, data3] = await Promise.all([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8'),
fs.readFile('file3.txt', 'utf8')
]);
console.log('文件1内容:', data1);
console.log('文件2内容:', data2);
console.log('文件3内容:', data3);
// 合并内容
const combinedContent = data1 + '\n' + data2 + '\n' + data3;
// 写入合并后的内容
await fs.writeFile('combined.txt', combinedContent);
console.log('文件合并完成');
return combinedContent;
} catch (error) {
console.error('并行操作时发生错误:', error);
throw error;
}
}
parallelFileOperations()
.then(result => {
console.log('合并后的内容长度:', result.length);
})
.catch(error => {
console.error('捕获到错误:', error);
});案例三:异步函数链式调用
const http = require('http');
// 封装 HTTP 请求为 Promise
function httpGetPromise(url) {
return new Promise((resolve, reject) => {
http.get(url, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
resolve(data);
});
response.on('error', (error) => {
reject(error);
});
}).on('error', (error) => {
reject(error);
});
});
}
// 使用 async/await 链式调用
async function fetchAndProcessData() {
try {
// 获取数据
const data = await httpGetPromise('http://example.com');
console.log('获取到数据,长度:', data.length);
// 处理数据
const processedData = await processData(data);
console.log('处理后的数据长度:', processedData.length);
// 进一步处理
const finalResult = await finalProcess(processedData);
console.log('最终结果:', finalResult);
return finalResult;
} catch (error) {
console.error('处理数据时发生错误:', error);
throw error;
}
}
// 模拟异步数据处理函数
async function processData(data) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
return data.substring(0, 1000);
}
// 模拟最终处理函数
async function finalProcess(data) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 500));
return `处理完成,数据前50个字符: ${data.substring(0, 50)}...`;
}
// 调用
fetchAndProcessData()
.then(result => {
console.log('操作完成:', result);
})
.catch(error => {
console.error('捕获到错误:', error);
});案例四:使用 async/await 重构 Promise 代码
const fs = require('fs').promises;
// Promise 版本
function processFilesPromise() {
return fs.readFile('file1.txt', 'utf8')
.then(data1 => {
return Promise.all([Promise.resolve(data1), fs.readFile('file2.txt', 'utf8')]);
})
.then(([data1, data2]) => {
const combined = data1 + data2;
return fs.writeFile('combined.txt', combined);
})
.then(() => {
return '文件处理完成';
})
.catch(err => {
console.error('处理文件时发生错误:', err);
throw err;
});
}
// async/await 版本
async function processFilesAsyncAwait() {
try {
// 并行读取两个文件
const [data1, data2] = await Promise.all([
fs.readFile('file1.txt', 'utf8'),
fs.readFile('file2.txt', 'utf8')
]);
// 合并内容
const combined = data1 + data2;
// 写入文件
await fs.writeFile('combined.txt', combined);
return '文件处理完成';
} catch (error) {
console.error('处理文件时发生错误:', error);
throw error;
}
}
// 调用两种版本
console.log('使用 Promise 处理文件:');
processFilesPromise()
.then(result => {
console.log(result);
})
.catch(error => {
console.error('捕获到错误:', error);
});
console.log('\n使用 async/await 处理文件:');
processFilesAsyncAwait()
.then(result => {
console.log(result);
})
.catch(error => {
console.error('捕获到错误:', error);
});学习目标
- 理解 async/await 概念:掌握 async/await 的基本原理和工作机制
- 掌握 async 函数:学会声明和使用 async 函数
- 熟练使用 await:能够在 async 函数中使用 await 等待 Promise 完成
- 掌握错误处理:学会在 async/await 中使用 try/catch 处理错误
- 并行执行操作:能够使用 Promise.all() 并行执行多个异步操作
- 重构代码:能够将 Promise 风格的代码重构为 async/await 风格
代码优化建议
1. 合理使用并行操作
不好的做法:
async function sequentialOperations() {
const result1 = await asyncOperation1();
const result2 = await asyncOperation2(); // 必须等待前一个操作完成
const result3 = await asyncOperation3(); // 必须等待前一个操作完成
return [result1, result2, result3];
}好的做法:
async function parallelOperations() {
const [result1, result2, result3] = await Promise.all([
asyncOperation1(),
asyncOperation2(),
asyncOperation3()
]);
return [result1, result2, result3];
}2. 正确处理错误
不好的做法:
async function errorProneFunction() {
const result = await somePromise(); // 没有错误处理
return result;
}好的做法:
async function safeFunction() {
try {
const result = await somePromise();
return result;
} catch (error) {
console.error('发生错误:', error);
// 可以选择:
// 1. 返回默认值
// 2. 重新抛出错误
// 3. 处理错误并继续
return null; // 返回默认值
}
}3. 避免在非异步函数中使用 await
不好的做法:
function regularFunction() {
const result = await somePromise(); // 错误:不能在非 async 函数中使用 await
return result;
}好的做法:
async function asyncFunction() {
const result = await somePromise();
return result;
}
// 或者在立即执行函数表达式中使用
(function() {
async function wrapper() {
const result = await somePromise();
console.log(result);
}
wrapper();
})();
// 或者使用 top-level await (Node.js 14.8+)
// await somePromise();4. 注意 async/await 的性能
不好的做法:
async function processArray(array) {
const results = [];
for (const item of array) {
const result = await processItem(item); // 串行处理
results.push(result);
}
return results;
}好的做法:
async function processArray(array) {
const promises = array.map(item => processItem(item)); // 并行处理
const results = await Promise.all(promises);
return results;
}常见问题与解决方案
问题1:await 只能在 async 函数中使用
原因:
await关键字只能在async函数内部使用
解决方案:
- 将函数声明为
async函数 - 使用立即执行的 async 函数表达式
- 在支持的环境中使用 top-level await
问题2:未处理的 Promise 拒绝
原因:
async函数返回的 Promise 被拒绝但没有被处理
解决方案:
- 始终处理
async函数返回的 Promise 的拒绝 - 使用 try/catch 处理
async函数内部的错误
问题3:async/await 导致的性能问题
原因:
- 串行执行多个独立的异步操作
解决方案:
- 使用
Promise.all()并行执行多个独立的异步操作 - 合理使用
Promise.race()设置超时
问题4:async/await 中的错误捕获
原因:
- 嵌套的 async 函数中的错误没有被正确捕获
解决方案:
- 确保每个
async函数都有错误处理 - 使用 try/catch 捕获所有可能的错误
总结
async/await 是 Node.js 中处理异步操作的现代方式,通过本教程的学习,你应该能够:
- 理解
async/await的基本概念和工作原理 - 掌握
async函数的声明和使用 - 熟练使用
await表达式等待 Promise 完成 - 正确处理
async/await中的错误 - 合理使用并行操作提高性能
- 将 Promise 风格的代码重构为
async/await风格
async/await 让异步代码的写法更接近同步代码,大大提高了代码的可读性和可维护性,是现代 Node.js 开发中的推荐做法。在实际项目中,你应该尽可能使用 async/await 来处理异步操作,同时注意合理使用并行操作来提高性能。