异步函数
异步函数(Async Functions)是 ES2017 引入的一个重要特性,它提供了一种更简洁、更易读的方式来处理异步操作。异步函数基于 Promise,使异步代码看起来像同步代码。
什么是异步函数?
异步函数是使用 async 关键字声明的函数,它会隐式返回一个 Promise。在异步函数内部,可以使用 await 关键字来等待 Promise 的解决。
基本语法
javascript
// 异步函数声明
async function myFunction() {
// 函数体
}
// 异步函数表达式
const myFunction = async function() {
// 函数体
};
// 异步箭头函数
const myFunction = async () => {
// 函数体
};
// 对象方法中的异步函数
const obj = {
async myMethod() {
// 方法体
}
};
// 类方法中的异步函数
class MyClass {
async myMethod() {
// 方法体
}
}await 关键字
await 关键字只能在异步函数内部使用,它会暂停函数的执行,等待 Promise 解决,然后返回结果。
javascript
async function fetchData() {
try {
// 等待 Promise 解决
let response = await fetch('https://api.example.com/data');
let data = await response.json();
return data;
} catch (error) {
// 处理错误
console.error('获取数据失败:', error);
throw error;
}
}
// 调用异步函数
fetchData().then(data => {
console.log('获取到的数据:', data);
}).catch(error => {
console.error('处理错误:', error);
});错误处理
异步函数中的错误处理可以使用 try/catch 语句,这比使用 Promise 的 .catch() 方法更直观。
javascript
async function example() {
try {
let result1 = await someAsyncOperation1();
let result2 = await someAsyncOperation2(result1);
let result3 = await someAsyncOperation3(result2);
return result3;
} catch (error) {
console.error('操作失败:', error);
// 可以选择重新抛出错误或返回默认值
throw error; // 重新抛出错误
// 或者 return defaultValue; // 返回默认值
}
}并行执行
在异步函数中,默认情况下 await 会顺序执行 Promise。如果希望并行执行多个不相关的异步操作,可以使用 Promise.all()。
javascript
// 顺序执行(较慢)
async function sequential() {
let result1 = await fetch('/api/data1');
let result2 = await fetch('/api/data2');
let result3 = await fetch('/api/data3');
return [result1, result2, result3];
}
// 并行执行(较快)
async function parallel() {
let [result1, result2, result3] = await Promise.all([
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
]);
return [result1, result2, result3];
}实际应用示例
获取用户数据
javascript
async function fetchUserData(userId) {
try {
// 获取用户基本信息
let userResponse = await fetch(`/api/users/${userId}`);
if (!userResponse.ok) {
throw new Error(`HTTP error! status: ${userResponse.status}`);
}
let user = await userResponse.json();
// 获取用户的文章
let postsResponse = await fetch(`/api/users/${userId}/posts`);
if (!postsResponse.ok) {
throw new Error(`HTTP error! status: ${postsResponse.status}`);
}
let posts = await postsResponse.json();
// 合并数据
return {
...user,
posts: posts
};
} catch (error) {
console.error('获取用户数据失败:', error);
throw new Error('无法获取用户数据');
}
}
// 使用示例
fetchUserData(123)
.then(userData => {
console.log('用户数据:', userData);
})
.catch(error => {
console.error('错误:', error.message);
});文件上传
javascript
async function uploadFile(file) {
try {
// 创建 FormData 对象
let formData = new FormData();
formData.append('file', file);
// 上传文件
let response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`上传失败: ${response.statusText}`);
}
let result = await response.json();
console.log('上传成功:', result);
return result;
} catch (error) {
console.error('上传过程中发生错误:', error);
throw error;
}
}数据库操作
javascript
// 模拟数据库操作
async function getUserById(id) {
// 模拟异步数据库查询
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id, name: `用户${id}`, email: `user${id}@example.com` });
}, 100);
});
}
async function updateUser(id, updates) {
try {
// 获取现有用户数据
let user = await getUserById(id);
// 应用更新
let updatedUser = { ...user, ...updates };
// 模拟保存到数据库
await new Promise(resolve => setTimeout(resolve, 200));
console.log('用户更新成功:', updatedUser);
return updatedUser;
} catch (error) {
console.error('更新用户失败:', error);
throw error;
}
}
// 使用示例
async function main() {
try {
let updatedUser = await updateUser(1, { name: '新名字' });
console.log('更新后的用户:', updatedUser);
} catch (error) {
console.error('主函数中捕获的错误:', error);
}
}与 Promise 的比较
使用 Promise
javascript
function fetchUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(user => {
return fetch(`/api/users/${userId}/posts`);
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(posts => {
return { user, posts };
})
.catch(error => {
console.error('获取用户数据失败:', error);
throw new Error('无法获取用户数据');
});
}使用异步函数
javascript
async function fetchUserData(userId) {
try {
let userResponse = await fetch(`/api/users/${userId}`);
if (!userResponse.ok) {
throw new Error(`HTTP error! status: ${userResponse.status}`);
}
let user = await userResponse.json();
let postsResponse = await fetch(`/api/users/${userId}/posts`);
if (!postsResponse.ok) {
throw new Error(`HTTP error! status: ${postsResponse.status}`);
}
let posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('获取用户数据失败:', error);
throw new Error('无法获取用户数据');
}
}注意事项和最佳实践
总是使用 try/catch:在异步函数中使用 try/catch 来处理错误。
避免在循环中顺序使用 await:如果需要处理多个不相关的异步操作,考虑并行执行。
javascript
// 不好的做法 - 顺序执行
async function badExample() {
let results = [];
for (let i = 0; i < urls.length; i++) {
let response = await fetch(urls[i]);
let data = await response.json();
results.push(data);
}
return results;
}
// 好的做法 - 并行执行
async function goodExample() {
let promises = urls.map(url => fetch(url).then(r => r.json()));
let results = await Promise.all(promises);
return results;
}- 正确处理 Promise 返回值:记住异步函数总是返回 Promise。
javascript
async function getData() {
return "数据";
}
// 正确用法
getData().then(data => console.log(data));
// 错误用法
// let data = getData(); // data 是一个 Promise,不是实际数据- 避免忘记 await:忘记使用 await 会导致获取到 Promise 而不是实际值。
javascript
async function example() {
// 错误 - 忘记 await
let data = fetch('/api/data');
console.log(data); // 输出: Promise {...}
// 正确
let data = await fetch('/api/data');
console.log(data); // 输出: Response 对象
}- 在顶层使用异步函数:在现代浏览器和 Node.js 中,可以在模块顶层使用 await。
javascript
// 在模块顶层使用 await (现代环境支持)
let data = await fetch('/api/data');
let jsonData = await data.json();
console.log(jsonData);异步函数极大地简化了异步编程,使代码更易读、更易维护。它是现代 JavaScript 开发中处理异步操作的首选方式。