02-异步编程
📋 学习目标
理解JavaScript事件循环机制
掌握Promise的使用和原理
精通async/await异步语法
理解并发控制和错误处理
🔄 事件循环
执行模型
// 同步任务
console.log('1');
// 宏任务(MacroTask)
setTimeout(() => {
console.log('2');
}, 0);
// 微任务(MicroTask)
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
// 输出顺序:1 4 3 2
任务队列
/*
执行顺序:
1. 同步代码
2. 微任务队列(Promise, MutationObserver)
3. 宏任务队列(setTimeout, setInterval, I/O)
*/
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('promise1');
})
.then(() => {
console.log('promise2');
});
console.log('script end');
// 输出:
// script start
// script end
// promise1
// promise2
// setTimeout
📞 回调函数
基本回调
// 简单回调
function getData(callback) {
setTimeout(() => {
callback({name: 'John', age: 30});
}, 1000);
}
getData((data) => {
console.log(data);
});
// Node.js错误优先回调
fs.readFile('file.txt', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
回调地狱
// 回调地狱示例
getUser(userId, (err, user) => {
if (err) {
handleError(err);
} else {
getOrders(user.id, (err, orders) => {
if (err) {
handleError(err);
} else {
getOrderDetails(orders[0].id, (err, details) => {
if (err) {
handleError(err);
} else {
// 继续嵌套...
}
});
}
});
}
});
🎁 Promise
基本用法
// 创建Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('Success!');
} else {
reject(new Error('Failed!'));
}
}, 1000);
});
// 使用Promise
promise
.then(result => {
console.log(result); // Success!
return 'Next value';
})
.then(result => {
console.log(result); // Next value
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('Cleanup');
});
Promise状态
/*
三种状态:
1. pending(进行中)
2. fulfilled(已成功)
3. rejected(已失败)
状态只能从pending改变为fulfilled或rejected
状态一旦改变就不会再变
*/
const p1 = Promise.resolve('Success'); // 立即fulfilled
const p2 = Promise.reject(new Error('Failed')); // 立即rejected
链式调用
function getUser(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({id, name: 'John'});
}, 1000);
});
}
function getOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{id: 1}, {id: 2}]);
}, 1000);
});
}
// 链式调用
getUser(1)
.then(user => {
console.log('User:', user);
return getOrders(user.id);
})
.then(orders => {
console.log('Orders:', orders);
})
.catch(error => {
console.error('Error:', error);
});
Promise静态方法
// Promise.all - 所有成功才成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3])
.then(results => {
console.log(results); // [1, 2, 3]
})
.catch(error => {
console.error('One failed:', error);
});
// Promise.allSettled - 等待所有完成
Promise.allSettled([p1, p2, Promise.reject('error')])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Success:', result.value);
} else {
console.log('Failed:', result.reason);
}
});
});
// Promise.race - 最快的一个
Promise.race([
fetch('/api/slow'),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 3000)
)
])
.then(result => console.log(result))
.catch(error => console.error(error));
// Promise.any - 任意一个成功
Promise.any([
fetch('/api/1').catch(() => 'API1 failed'),
fetch('/api/2').catch(() => 'API2 failed'),
fetch('/api/3').catch(() => 'API3 failed')
])
.then(result => console.log('First success:', result))
.catch(() => console.log('All failed'));
Promise实现
class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'
? onFulfilled
: value => value;
onRejected = typeof onRejected === 'function'
? onRejected
: reason => { throw reason };
return new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
});
}
});
}
}
🚀 Async/Await
基本用法
// async函数返回Promise
async function getData() {
return 'data';
}
getData().then(data => console.log(data)); // data
// await等待Promise
async function fetchUser() {
const response = await fetch('/api/user');
const user = await response.json();
return user;
}
// 等同于
function fetchUser() {
return fetch('/api/user')
.then(response => response.json());
}
错误处理
// try-catch捕获错误
async function getData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
return null;
}
}
// 统一错误处理
async function request(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
async function main() {
try {
const data = await request('/api/data');
console.log(data);
} catch (error) {
console.error('Failed:', error.message);
}
}
// 顶层await(ES2022)
const data = await fetch('/api/data').then(r => r.json());
并发执行
// ❌ 串行执行(慢)
async function sequential() {
const user = await fetchUser(); // 1秒
const orders = await fetchOrders(); // 1秒
const products = await fetchProducts(); // 1秒
return {user, orders, products}; // 总共3秒
}
// ✅ 并发执行(快)
async function concurrent() {
const [user, orders, products] = await Promise.all([
fetchUser(),
fetchOrders(),
fetchProducts()
]);
return {user, orders, products}; // 总共1秒
}
// 部分依赖
async function partial() {
const user = await fetchUser();
// 这两个可以并发
const [orders, profile] = await Promise.all([
fetchOrders(user.id),
fetchProfile(user.id)
]);
return {user, orders, profile};
}
循环中的异步
// ❌ forEach不等待
async function wrong() {
const ids = [1, 2, 3];
ids.forEach(async (id) => {
const data = await fetchData(id);
console.log(data);
});
console.log('Done'); // 会立即执行
}
// ✅ for...of串行执行
async function serial() {
const ids = [1, 2, 3];
for (const id of ids) {
const data = await fetchData(id);
console.log(data);
}
console.log('Done');
}
// ✅ Promise.all并发执行
async function parallel() {
const ids = [1, 2, 3];
const promises = ids.map(id => fetchData(id));
const results = await Promise.all(promises);
results.forEach(data => console.log(data));
console.log('Done');
}
// ✅ map + Promise.all
async function mapAsync() {
const ids = [1, 2, 3];
const results = await Promise.all(
ids.map(async (id) => {
const data = await fetchData(id);
return processData(data);
})
);
return results;
}
🔧 实战技巧
超时控制
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
}
async function fetchWithTimeout(url, ms = 3000) {
return Promise.race([
fetch(url),
timeout(ms)
]);
}
// 使用
try {
const response = await fetchWithTimeout('/api/data', 5000);
const data = await response.json();
} catch (error) {
console.error('Request failed or timeout:', error);
}
重试机制
async function retry(fn, maxAttempts = 3, delay = 1000) {
for (let i = 0; i < maxAttempts; i++) {
try {
return await fn();
} catch (error) {
if (i === maxAttempts - 1) throw error;
console.log(`Attempt ${i + 1} failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// 使用
const data = await retry(() => fetch('/api/data'), 3, 2000);
并发限制
class PromisePool {
constructor(maxConcurrency) {
this.maxConcurrency = maxConcurrency;
this.currentCount = 0;
this.queue = [];
}
async add(promiseFactory) {
while (this.currentCount >= this.maxConcurrency) {
await Promise.race(this.queue);
}
this.currentCount++;
const promise = promiseFactory();
const queueItem = promise.then(
() => this.remove(queueItem),
() => this.remove(queueItem)
);
this.queue.push(queueItem);
return promise;
}
remove(promise) {
const index = this.queue.indexOf(promise);
this.queue.splice(index, 1);
this.currentCount--;
}
}
// 使用
const pool = new PromisePool(3);
const tasks = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const results = await Promise.all(
tasks.map(id => pool.add(() => fetchData(id)))
);
取消请求
// 使用AbortController
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/data', {signal})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request cancelled');
}
});
// 取消请求
controller.abort();
// 超时取消
async function fetchWithAbort(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {signal: controller.signal});
clearTimeout(timeoutId);
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
缓存Promise
const cache = new Map();
function getCachedData(key, fetcher) {
if (!cache.has(key)) {
cache.set(key, fetcher());
}
return cache.get(key);
}
// 使用
const userData = await getCachedData('user:1', () => fetchUser(1));
// 带过期时间的缓存
class PromiseCache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl;
}
async get(key, fetcher) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.value;
}
const value = await fetcher();
this.cache.set(key, {
value,
timestamp: Date.now()
});
return value;
}
}
📚 实践练习
练习1:Promise实现
手动实现:
Promise.all
Promise.race
Promise.allSettled
Promise.any
练习2:异步工具函数
实现以下工具:
带超时的fetch
重试机制
并发控制
请求队列
练习3:实际应用
创建一个数据获取层:
支持缓存
支持取消
支持重试
错误处理
💡 最佳实践
优先使用async/await而不是Promise链
注意Promise.all的失败处理
合理使用并发和串行
总是处理异步错误
避免在循环中使用await(除非需要串行)