📖 核心概念
- JavaScript事件循环机制
- 回调函数与回调地狱
- Promise:链式调用与错误处理
- async/await:同步写法的异步
- Promise.all/race/allSettled/any
- AbortController与取消异步
💻 代码实现
Promise与async/await
✅ javascript
// Promise基础 - 封装API请求
function request(url, options = {}) {
return new Promise((resolve, reject) => {
const controller = new AbortController();
options.signal = controller.signal;
// 5秒超时
const timeout = setTimeout(() => controller.abort(), 5000);
fetch(url, options)
.then(res => {
clearTimeout(timeout);
if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
return res.json();
})
.then(resolve)
.catch(reject);
});
}
// async/await - 优雅的异步代码
async function loadUserProfile(userId) {
try {
const [profile, posts, followers] = await Promise.all([
request(\`/api/users/\${userId}\`),
request(\`/api/users/\${userId}/posts\`),
request(\`/api/users/\${userId}/followers\`),
]);
return { profile, posts, followers };
} catch (error) {
if (error.name === 'AbortError') {
console.error('请求被取消或超时');
} else {
console.error('加载失败:', error.message);
}
throw error;
}
}
// Promise组合器
async function loadDashboard() {
// all: 全部成功才成功
const essential = await Promise.all([
request('/api/user'),
request('/api/config'),
]);
// allSettled: 无论成败都等
const optional = await Promise.allSettled([
request('/api/notifications'),
request('/api/recommendations'),
request('/api/analytics'),
]);
const succeeded = optional
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
return { essential, optional: succeeded };
}
// 重试机制
async function retry(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, delay * (i + 1)));
console.log(\`重试第 \${i + 1} 次...\`);
}
}
}
异步迭代与并发控制
✅ javascript
// 异步迭代器
async function* fetchPages(baseUrl) {
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await request(\`\${baseUrl}?page=\${page}\`);
yield response.data;
hasMore = response.hasNext;
page++;
}
}
// 使用 for await...of
async function loadAllPosts() {
const allPosts = [];
for await (const posts of fetchPages('/api/posts')) {
allPosts.push(...posts);
}
return allPosts;
}
// 并发控制 - 限制同时进行的请求数
async function concurrentLimit(tasks, limit) {
const results = [];
const executing = new Set();
for (const [i, task] of tasks.entries()) {
const p = task().then(result => {
executing.delete(p);
return result;
});
executing.add(p);
results[i] = p;
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
// 使用:最多3个并发请求
const urls = Array.from({ length: 10 }, (_, i) =>
() => request(\`/api/item/\${i}\`)
);
const items = await concurrentLimit(urls, 3);
// 可取消的异步操作
class CancellationToken {
#aborted = false;
#listeners = new Set();
get aborted() { return this.#aborted; }
abort() {
this.#aborted = true;
this.#listeners.forEach(fn => fn());
}
onAbort(fn) {
this.#listeners.add(fn);
}
}
async function longOperation(token) {
for (let i = 0; i < 100; i++) {
if (token.aborted) throw new Error('操作已取消');
await new Promise(r => setTimeout(r, 100));
}
return '完成';
}
const token = new CancellationToken();
setTimeout(() => token.abort(), 3000); // 3秒后取消
try {
await longOperation(token);
} catch (e) {
console.log(e.message); // "操作已取消"
}
🔍 异步编程陷阱与对策
常见陷阱:忘记await导致Promise未解析、在循环中使用await而非Promise.all、未处理的Promise rejection、async构造函数问题。最佳实践:始终使用try/catch包裹await、使用Promise.all处理并行异步、全局注册unhandledrejection事件处理。
🎯 练习任务
- 1 将一个回调地狱代码重写为async/await
- 2 实现一个带进度追踪的Promise.all
- 3 创建一个并发请求池,支持优先级队列
- 4 实现带指数退避的重试机制
🏆 成就解锁
异步高手 — 掌握JavaScript异步编程全链路,写出优雅高效的异步代码