为什么需要异步编程
JavaScript 是单线程语言,异步编程让我们可以在等待耗时操作(如网络请求)时,不阻塞主线程的执行。
回调函数
回调函数是最早的异步编程方式。
// 回调函数示例
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: 'John' };
callback(null, data);
}, 1000);
}
fetchData((error, data) => {
if (error) {
console.error(error);
} else {
console.log(data);
}
});
回调地狱
// 嵌套回调导致代码难以维护
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
getMoreData(c, function(d) {
console.log(d);
});
});
});
});
Promise
Promise 是 ES6 引入的异步编程解决方案,解决了回调地狱问题。
// 创建 Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log(result);
return '下一个值';
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('无论成功失败都会执行');
});
Promise 链式调用
// 使用 Promise 解决回调地狱
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => getMoreData(c))
.then(d => console.log(d))
.catch(error => console.error(error));
Promise.all 和 Promise.race
// 并行执行多个 Promise
const promises = [
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
];
Promise.all(promises)
.then(([users, posts, comments]) => {
console.log('所有请求完成');
})
.catch(error => {
console.error('任一请求失败');
});
// 返回最快完成的 Promise
Promise.race(promises)
.then(result => {
console.log('最快的请求完成');
});
Async/Await
async/await 是 ES2017 引入的语法糖,让异步代码看起来像同步代码。
// async 函数
async function getUserData() {
try {
const response = await fetch('/api/user');
const user = await response.json();
return user;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 使用 async 函数
async function init() {
const user = await getUserData();
console.log(user);
}
init();
并行执行
async function fetchAllData() {
// 串行执行(慢)
// const users = await fetchUsers();
// const posts = await fetchPosts();
// 并行执行(快)
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
return { users, posts };
}
错误处理
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 ${response.status}`);
}
return await response.json();
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
console.log(`第 ${i + 1} 次重试...`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
总结
从回调函数到 Promise 再到 async/await,JavaScript 的异步编程越来越优雅。建议在新项目中优先使用 async/await,它让代码更易读、更易维护。