1.31 - async/await原理、Generator
JavaScript async/await 与 Generator 深度解析
目录
前置知识:异步编程的演进
1.1 JavaScript 异步发展史
// 第一代:回调函数(Callback)
setTimeout(function() {
console.log('延迟执行');
}, 1000);
// 第二代:Promise
new Promise((resolve, reject) => {
setTimeout(() => resolve('成功'), 1000);
}).then(result => console.log(result));
// 第三代:Generator(ES6)
function* gen() {
const result = yield fetchData();
console.log(result);
}
// 第四代:async/await(ES2017)
async function fetchUser() {
const user = await getUserData();
return user;
}
1.2 为什么需要 Generator 和 async/await?
回调地狱问题:
// 回调地狱示例
getUserId(function(userId) {
getUserData(userId, function(userData) {
getUserOrders(userData.id, function(orders) {
getOrderDetails(orders[0].id, function(details) {
console.log(details);
// 继续嵌套...
});
});
});
});
解决方案对比:
// Promise 链式调用
getUserId()
.then(userId => getUserData(userId))
.then(userData => getUserOrders(userData.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => console.log(details))
.catch(error => console.error(error));
// async/await(最优雅)
async function fetchUserOrders() {
try {
const userId = await getUserId();
const userData = await getUserData(userId);
const orders = await getUserOrders(userData.id);
const details = await getOrderDetails(orders[0].id);
console.log(details);
} catch (error) {
console.error(error);
}
}
Generator 函数深度剖析
2.1 Generator 基本概念
Generator 是 ES6 引入的一种可以暂停执行的函数,它返回一个迭代器对象。
// 基本语法
function* generatorFunction() {
console.log('开始执行');
yield 'first';
console.log('继续执行');
yield 'second';
console.log('最后执行');
return 'done';
}
// 使用
const gen = generatorFunction(); // 不会立即执行
console.log(gen.next()); // { value: 'first', done: false }
console.log(gen.next()); // { value: 'second', done: false }
console.log(gen.next()); // { value: 'done', done: true }
关键特性:
- 使用
function*声明 - 内部使用
yield暂停执行 - 调用后返回迭代器对象
- 通过
next()方法恢复执行
2.2 Generator 执行机制
function* example() {
console.log('Step 1');
const a = yield 1;
console.log('Step 2, a =', a);
const b = yield 2;
console.log('Step 3, b =', b);
return 3;
}
const iter = example();
// 第一次调用 next()
console.log(iter.next());
// 输出: "Step 1"
// 返回: { value: 1, done: false }
// 第二次调用 next(10)
console.log(iter.next(10));
// 输出: "Step 2, a = 10"
// 返回: { value: 2, done: false }
// 第三次调用 next(20)
console.log(iter.next(20));
// 输出: "Step 3, b = 20"
// 返回: { value: 3, done: true }
执行流程图:
调用 example() → 返回迭代器(未执行函数体)
第一次 next() → 执行到第一个 yield,返回 1
第二次 next(10) → 将 10 赋值给 a,执行到第二个 yield,返回 2
第三次 next(20) → 将 20 赋值给 b,执行到 return,返回 3
2.3 yield 的双向通信
function* dataFlow() {
const input1 = yield 'What is your name?';
const input2 = yield `Hello ${input1}, how old are you?`;
return `${input1} is ${input2} years old`;
}
const conversation = dataFlow();
console.log(conversation.next().value); // "What is your name?"
console.log(conversation.next('Alice').value); // "Hello Alice, how old are you?"
console.log(conversation.next(25).value); // "Alice is 25 years old"
关键理解点:
yield右侧的值会通过next()返回给外部- 下一次
next(value)的参数会赋值给上一个yield表达式 - 第一次
next()的参数会被忽略(因为没有上一个 yield)
2.4 Generator 的高级用法
2.4.1 yield* 委托
function* inner() {
yield 'inner-1';
yield 'inner-2';
}
function* outer() {
yield 'outer-1';
yield* inner(); // 委托给另一个 Generator
yield 'outer-2';
}
const gen = outer();
console.log([...gen]);
// ['outer-1', 'inner-1', 'inner-2', 'outer-2']
2.4.2 错误处理
function* errorHandler() {
try {
yield 'start';
yield 'middle';
} catch (e) {
console.log('捕获错误:', e);
}
yield 'end';
}
const gen = errorHandler();
console.log(gen.next()); // { value: 'start', done: false }
console.log(gen.throw('出错了!')); // 捕获错误: 出错了!
// { value: 'end', done: false }
2.4.3 提前结束
function* cancellable() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log('清理资源');
}
}
const gen = cancellable();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.return()); // 清理资源
// { value: undefined, done: true }
2.5 Generator 实现异步控制流
// 手动执行 Generator 处理异步
function* fetchUser() {
const user = yield fetch('https://api.example.com/user');
const orders = yield fetch(`https://api.example.com/orders/${user.id}`);
return { user, orders };
}
// 执行器函数
function run(generatorFunc) {
const gen = generatorFunc();
function step(nextValue) {
const result = gen.next(nextValue);
if (result.done) {
return result.value;
}
// 假设 yield 返回的是 Promise
result.value.then(
value => step(value),
error => gen.throw(error)
);
}
step();
}
// 使用
run(fetchUser);
async/await 原理解析
3.1 async/await 本质
核心概念:async/await 是 Generator + Promise 的语法糖
// async/await 写法
async function fetchData() {
const data1 = await fetch('/api/data1');
const data2 = await fetch('/api/data2');
return [data1, data2];
}
// 等价的 Generator 实现
function* fetchDataGenerator() {
const data1 = yield fetch('/api/data1');
const data2 = yield fetch('/api/data2');
return [data1, data2];
}
// 需要配合执行器
function runGenerator(gen) {
return new Promise((resolve, reject) => {
const g = gen();
function step(nextValue) {
let result;
try {
result = g.next(nextValue);
} catch (e) {
return reject(e);
}
if (result.done) {
return resolve(result.value);
}
Promise.resolve(result.value).then(
value => step(value),
error => {
try {
step(g.throw(error));
} catch (e) {
reject(e);
}
}
);
}
step();
});
}
// 使用
runGenerator(fetchDataGenerator);
3.2 async 函数的特性
// 1. async 函数总是返回 Promise
async function test1() {
return 'hello';
}
console.log(test1()); // Promise { 'hello' }
// 2. 等价于
function test2() {
return Promise.resolve('hello');
}
// 3. throw 错误会返回 rejected Promise
async function test3() {
throw new Error('出错了');
}
test3().catch(e => console.log(e)); // Error: 出错了
// 4. async 函数内部的 return 值会成为 then 的参数
async function test4() {
return { name: 'Alice', age: 25 };
}
test4().then(data => console.log(data)); // { name: 'Alice', age: 25 }
3.3 await 的执行机制
async function example() {
console.log('1');
const result1 = await Promise.resolve('2');
console.log(result1);
const result2 = await Promise.resolve('3');
console.log(result2);
console.log('4');
}
console.log('0');
example();
console.log('5');
// 输出顺序: 0, 1, 5, 2, 3, 4
执行流程分析:
1. 同步代码: console.log('0')
2. 调用 example()
3. 同步代码: console.log('1')
4. 遇到 await,暂停,将后续代码放入微任务队列
5. 跳出函数,继续执行: console.log('5')
6. 主线程执行完毕,执行微任务队列
7. 恢复 example 执行: console.log('2')
8. 再次遇到 await,暂停
9. 执行下一个微任务: console.log('3')
10. 最后执行: console.log('4')
3.4 await 后面跟不同类型值
async function awaitTest() {
// 1. await Promise
const p1 = await Promise.resolve(1);
console.log(p1); // 1
// 2. await thenable 对象
const thenable = {
then(resolve, reject) {
resolve(2);
}
};
const p2 = await thenable;
console.log(p2); // 2
// 3. await 普通值(会被包装成 Promise.resolve)
const p3 = await 3;
console.log(p3); // 3
// 4. await rejected Promise
try {
await Promise.reject('error');
} catch (e) {
console.log(e); // 'error'
}
}
3.5 错误处理机制
// 方式1: try-catch
async function method1() {
try {
const data = await fetch('/api/data');
return data;
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
// 方式2: Promise catch
async function method2() {
const data = await fetch('/api/data').catch(error => {
console.error('请求失败:', error);
return null;
});
return data;
}
// 方式3: 统一错误处理
function asyncWrapper(fn) {
return function(...args) {
return fn(...args).catch(error => {
console.error('异步错误:', error);
// 可以上报错误到监控系统
throw error;
});
};
}
const safeRequest = asyncWrapper(async function() {
const data = await fetch('/api/data');
return data;
});
底层实现与源码分析
4.1 手写 Generator 执行器(co 库原理)
/**
* 自动执行 Generator 函数的执行器
* 类似于 co 库的简化实现
*/
function co(generatorFunc) {
return new Promise((resolve, reject) => {
const gen = generatorFunc();
function step(nextValue) {
let result;
// 执行 next() 并捕获可能的错误
try {
result = gen.next(nextValue);
} catch (e) {
return reject(e);
}
// 如果 Generator 执行完毕
if (result.done) {
return resolve(result.value);
}
// 将 yield 的值转换为 Promise
Promise.resolve(result.value)
.then(
value => step(value), // 成功时继续下一步
error => { // 失败时抛入 Generator
let result;
try {
result = gen.throw(error);
} catch (e) {
return reject(e);
}
step(result.value);
}
);
}
step(); // 启动执行
});
}
// 使用示例
function* fetchData() {
const user = yield fetch('/api/user');
const orders = yield fetch(`/api/orders/${user.id}`);
return { user, orders };
}
co(fetchData)
.then(result => console.log(result))
.catch(error => console.error(error));
4.2 手写 async/await(Babel 转译原理)
/**
* 模拟 Babel 如何转译 async/await
*/
// 原始 async 函数
async function asyncFunc() {
const result1 = await promise1();
const result2 = await promise2(result1);
return result2;
}
// Babel 转译后的代码(简化版)
function asyncFunc() {
return _asyncToGenerator(function* () {
const result1 = yield promise1();
const result2 = yield promise2(result1);
return result2;
})();
}
// _asyncToGenerator 辅助函数
function _asyncToGenerator(generatorFunc) {
return function() {
const gen = generatorFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = gen[key](arg); // gen.next(arg) 或 gen.throw(arg)
} catch (error) {
return reject(error);
}
const { value, done } = result;
if (done) {
return resolve(value);
}
// 确保 value 是 Promise
return Promise.resolve(value).then(
val => step('next', val),
err => step('throw', err)
);
}
step('next');
});
};
}
4.3 完整的 async/await polyfill
/**
* 生产级别的 async/await 实现
* 包含完整的错误处理和边界情况
*/
function asyncToGenerator(generatorFunc) {
return function() {
const self = this;
const args = arguments;
return new Promise((resolve, reject) => {
const gen = generatorFunc.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
function _throw(error) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', error);
}
_next(undefined);
});
};
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
let info, value;
try {
info = gen[key](arg);
value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
// 使用示例
const asyncFunc = asyncToGenerator(function* () {
const result1 = yield fetch('/api/data1');
const result2 = yield fetch('/api/data2');
return [result1, result2];
});
asyncFunc().then(console.log).catch(console.error);
4.4 Generator 状态机原理
/**
* Generator 函数的状态机实现原理
*/
// 原始 Generator
function* simpleGenerator() {
yield 1;
yield 2;
return 3;
}
// 转换为状态机(简化版)
function simpleGeneratorStateMachine() {
let state = 0; // 当前状态
return {
next(value) {
switch (state) {
case 0:
state = 1;
return { value: 1, done: false };
case 1:
state = 2;
return { value: 2, done: false };
case 2:
state = 3;
return { value: 3, done: true };
default:
return { value: undefined, done: true };
}
},
throw(error) {
throw error;
},
return(value) {
state = 3;
return { value, done: true };
}
};
}
// 测试
const gen1 = simpleGenerator();
const gen2 = simpleGeneratorStateMachine();
console.log(gen1.next()); // { value: 1, done: false }
console.log(gen2.next()); // { value: 1, done: false }
4.5 复杂 Generator 的状态机转换
/**
* 包含变量和控制流的 Generator 状态机
*/
// 原始代码
function* complexGenerator(x) {
const a = yield x + 1;
const b = yield a + 2;
return b + 3;
}
// 状态机实现
function complexGeneratorStateMachine(x) {
let state = 0;
let a, b;
return {
next(value) {
switch (state) {
case 0:
state = 1;
return { value: x + 1, done: false };
case 1:
a = value; // 接收上次 yield 的输入
state = 2;
return { value: a + 2, done: false };
case 2:
b = value;
state = 3;
return { value: b + 3, done: true };
default:
return { value: undefined, done: true };
}
}
};
}
// 测试
const gen = complexGeneratorStateMachine(10);
console.log(gen.next()); // { value: 11, done: false }
console.log(gen.next(20)); // { value: 22, done: false }
console.log(gen.next(30)); // { value: 33, done: true }
常见面试题与最佳实践
5.1 经典面试题解析
题目1: async/await 执行顺序
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
console.log('script end');
// 输出顺序:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
详细分析:
1. 同步代码: "script start"
2. setTimeout 进入宏任务队列
3. 执行 async1():
- 输出 "async1 start"
- 调用 async2(): 输出 "async2"
- await 暂停,后续代码进入微任务队列
4. 执行 Promise 构造函数: "promise1"
5. then 进入微任务队列
6. 同步代码: "script end"
7. 执行微任务队列:
- async1 恢复: "async1 end"
- Promise then: "promise2"
8. 执行宏任务队列: "setTimeout"
题目2: 多个 await 的执行时机
async function test() {
console.log('1');
await console.log('2');
console.log('3');
}
test();
console.log('4');
// 输出: 1, 2, 4, 3
// 为什么?
// await console.log('2') 会立即执行 console.log('2')
// 但 await 会暂停后续代码,将 console.log('3') 放入微任务
题目3: await 后面是非 Promise 值
async function test() {
const a = await 1; // 相当于 await Promise.resolve(1)
console.log(a);
const b = await {
then(resolve) {
resolve(2);
}
};
console.log(b);
}
test(); // 输出: 1, 2
题目4: 并发与串行的区别
// 串行执行(耗时 3秒)
async function serial() {
const result1 = await request1(); // 1秒
const result2 = await request2(); // 1秒
const result3 = await request3(); // 1秒
return [result1, result2, result3];
}
// 并发执行(耗时 1秒)
async function parallel() {
const [result1, result2, result3] = await Promise.all([
request1(),
request2(),
request3()
]);
return [result1, result2, result3];
}
// 或者
async function parallel2() {
const promise1 = request1(); // 立即发起请求
const promise2 = request2(); // 立即发起请求
const promise3 = request3(); // 立即发起请求
const result1 = await promise1;
const result2 = await promise2;
const result3 = await promise3;
return [result1, result2, result3];
}
5.2 常见陷阱与解决方案
陷阱1: forEach 中使用 await
// ❌ 错误:forEach 不会等待
async function processArray(array) {
array.forEach(async (item) => {
await asyncOperation(item);
});
console.log('Done'); // 会立即执行,不等待
}
// ✅ 正确方法1: for...of
async function processArray(array) {
for (const item of array) {
await asyncOperation(item);
}
console.log('Done'); // 等待所有操作完成
}
// ✅ 正确方法2: Promise.all(并发执行)
async function processArray(array) {
await Promise.all(array.map(item => asyncOperation(item)));
console.log('Done');
}
// ✅ 正确方法3: reduce(串行执行)
async function processArray(array) {
await array.reduce(async (promise, item) => {
await promise;
await asyncOperation(item);
}, Promise.resolve());
console.log('Done');
}
陷阱2: try-catch 只能捕获直接 await 的错误
// ❌ 无法捕获错误
async function wrong() {
try {
const promise = asyncOperation(); // 未 await
// ... 其他代码
return promise; // 错误会被传递到外部
} catch (error) {
console.log('捕获不到');
}
}
// ✅ 正确捕获
async function correct() {
try {
const result = await asyncOperation(); // 直接 await
return result;
} catch (error) {
console.log('成功捕获:', error);
}
}
陷阱3: 返回值的差异
// 情况1: 返回 Promise
async function case1() {
return Promise.resolve(1); // 返回 Promise<1>
}
// 情况2: await 后返回
async function case2() {
return await Promise.resolve(1); // 返回 Promise<1>
}
// 情况3: 直接返回值
async function case3() {
return 1; // 返回 Promise<1>
}
// 看起来相同,但性能有差异
// case2 会多一次 Promise 展开,效率略低
5.3 性能优化技巧
技巧1: 避免不必要的 await
// ❌ 不必要的 await
async function bad() {
return await fetch('/api/data'); // 多余的 await
}
// ✅ 直接返回 Promise
async function good() {
return fetch('/api/data');
}
// 例外:需要 try-catch 时必须使用 await
async function needAwait() {
try {
return await fetch('/api/data'); // 必要的 await
} catch (error) {
console.error(error);
return null;
}
}
技巧2: 合理使用并发
// 场景:获取用户信息和订单,它们互不依赖
// ❌ 串行执行(慢)
async function getDataSerial(userId) {
const user = await fetchUser(userId); // 等待 1s
const orders = await fetchOrders(userId); // 再等待 1s
return { user, orders }; // 总共 2s
}
// ✅ 并发执行(快)
async function getDataParallel(userId) {
const [user, orders] = await Promise.all([
fetchUser(userId),
fetchOrders(userId)
]);
return { user, orders }; // 总共 1s
}
// ✅ 部分并发(有依赖关系时)
async function getDataMixed(userId) {
const user = await fetchUser(userId);
// 这两个依赖 user,但彼此独立
const [orders, preferences] = await Promise.all([
fetchOrders(user.id),
fetchPreferences(user.id)
]);
return { user, orders, preferences };
}
技巧3: 使用 Promise.allSettled 处理部分失败
// 场景:批量请求,部分失败不影响其他
// ❌ Promise.all 一个失败全部失败
async function bad(urls) {
try {
const results = await Promise.all(urls.map(url => fetch(url)));
return results;
} catch (error) {
// 一个失败,所有结果丢失
console.error('请求失败');
return [];
}
}
// ✅ Promise.allSettled 获取所有结果
async function good(urls) {
const results = await Promise.allSettled(urls.map(url => fetch(url)));
const successful = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failed = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
console.log(`成功 ${successful.length} 个,失败 ${failed.length} 个`);
return successful;
}
5.4 高级应用模式
模式1: 异步队列
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(asyncFunc) {
while (this.running >= this.concurrency) {
await new Promise(resolve => this.queue.push(resolve));
}
this.running++;
try {
return await asyncFunc();
} finally {
this.running--;
const resolve = this.queue.shift();
if (resolve) resolve();
}
}
}
// 使用
const queue = new AsyncQueue(2); // 最多2个并发
async function fetchData(id) {
return queue.add(async () => {
const data = await fetch(`/api/data/${id}`);
return data;
});
}
// 10个请求,但同时只有2个在执行
const results = await Promise.all(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(id => fetchData(id))
);
模式2: 重试机制
async function retry(asyncFunc, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await asyncFunc();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`第 ${i + 1} 次重试失败,${delay}ms 后重试`);
await new Promise(resolve => setTimeout(resolve, delay));
// 指数退避
delay *= 2;
}
}
}
// 使用
const data = await retry(() => fetch('/api/unstable'), 5);
模式3: 超时控制
function timeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('超时')), ms)
)
]);
}
// 使用
async function fetchWithTimeout() {
try {
const data = await timeout(fetch('/api/slow'), 5000);
return data;
} catch (error) {
console.error('请求超时或失败:', error);
return null;
}
}
模式4: 取消 Promise
class CancelablePromise {
constructor(executor) {
this.isCanceled = false;
this.promise = new Promise((resolve, reject) => {
executor(
value => {
if (!this.isCanceled) resolve(value);
},
error => {
if (!this.isCanceled) reject(error);
}
);
});
}
cancel() {
this.isCanceled = true;
}
}
// 使用
const cancelable = new CancelablePromise((resolve, reject) => {
setTimeout(() => resolve('完成'), 5000);
});
setTimeout(() => {
cancelable.cancel();
console.log('操作已取消');
}, 2000);
cancelable.promise.then(
result => console.log(result),
error => console.error(error)
);
实战应用场景
6.1 数据请求场景
场景1: 分页加载
class PaginationLoader {
constructor(fetchFunc) {
this.fetchFunc = fetchFunc;
this.page = 1;
this.hasMore = true;
this.loading = false;
}
async loadMore() {
if (!this.hasMore || this.loading) return;
this.loading = true;
try {
const data = await this.fetchFunc(this.page);
if (data.length === 0) {
this.hasMore = false;
} else {
this.page++;
}
return data;
} finally {
this.loading = false;
}
}
async loadAll() {
const allData = [];
while (this.hasMore) {
const data = await this.loadMore();
if (data) allData.push(...data);
}
return allData;
}
}
// 使用
const loader = new PaginationLoader(async (page) => {
const response = await fetch(`/api/items?page=${page}`);
return response.json();
});
const allItems = await loader.loadAll();
场景2: 依赖请求链
async function fetchUserFullData(userId) {
// 1. 获取用户基本信息
const user = await fetchUser(userId);
// 2. 基于用户信息,并发获取相关数据
const [profile, orders, preferences] = await Promise.all([
fetchUserProfile(user.profileId),
fetchUserOrders(user.id),
fetchUserPreferences(user.id)
]);
// 3. 基于订单,获取订单详情(串行)
const orderDetails = [];
for (const order of orders.slice(0, 5)) { // 只获取前5个
const detail = await fetchOrderDetail(order.id);
orderDetails.push(detail);
}
return {
user,
profile,
orders,
orderDetails,
preferences
};
}
6.2 文件处理场景
class FileUploader {
constructor(options = {}) {
this.chunkSize = options.chunkSize || 1024 * 1024; // 1MB
this.concurrency = options.concurrency || 3;
}
async uploadFile(file) {
const chunks = this.splitFile(file);
const uploadedChunks = [];
// 分片并发上传
for (let i = 0; i < chunks.length; i += this.concurrency) {
const batch = chunks.slice(i, i + this.concurrency);
const results = await Promise.all(
batch.map((chunk, index) =>
this.uploadChunk(chunk, i + index, chunks.length)
)
);
uploadedChunks.push(...results);
}
// 合并分片
return await this.mergeChunks(file.name, uploadedChunks);
}
splitFile(file) {
const chunks = [];
let start = 0;
while (start < file.size) {
const end = Math.min(start + this.chunkSize, file.size);
chunks.push(file.slice(start, end));
start = end;
}
return chunks;
}
async uploadChunk(chunk, index, total) {
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('index', index);
formData.append('total', total);
const response = await fetch('/api/upload/chunk', {
method: 'POST',
body: formData
});
return response.json();
}
async mergeChunks(filename, chunks) {
const response = await fetch('/api/upload/merge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename, chunks })
});
return response.json();
}
}
// 使用
const uploader = new FileUploader({ concurrency: 5 });
const result = await uploader.uploadFile(file);
6.3 状态管理场景
class AsyncStateManager {
constructor() {
this.state = {};
this.listeners = new Map();
this.loadingStates = new Map();
}
async fetchAndUpdate(key, fetchFunc) {
if (this.loadingStates.get(key)) {
// 防止重复请求
return this.loadingStates.get(key);
}
const promise = (async () => {
try {
const data = await fetchFunc();
this.setState(key, data);
return data;
} finally {
this.loadingStates.delete(key);
}
})();
this.loadingStates.set(key, promise);
return promise;
}
setState(key, value) {
this.state[key] = value;
this.notify(key, value);
}
subscribe(key, callback) {
if (!this.listeners.has(key)) {
this.listeners.set(key, new Set());
}
this.listeners.get(key).add(callback);
// 返回取消订阅函数
return () => {
this.listeners.get(key).delete(callback);
};
}
notify(key, value) {
const callbacks = this.listeners.get(key);
if (callbacks) {
callbacks.forEach(cb => cb(value));
}
}
}
// 使用
const store = new AsyncStateManager();
// 订阅状态变化
const unsubscribe = store.subscribe('user', (user) => {
console.log('用户更新:', user);
});
// 获取数据
await store.fetchAndUpdate('user', () => fetch('/api/user').then(r => r.json()));
6.4 React Hooks 集成
// 自定义异步 Hook
function useAsync(asyncFunc, dependencies = []) {
const [state, setState] = React.useState({
loading: false,
data: null,
error: null
});
React.useEffect(() => {
let canceled = false;
(async () => {
setState({ loading: true, data: null, error: null });
try {
const data = await asyncFunc();
if (!canceled) {
setState({ loading: false, data, error: null });
}
} catch (error) {
if (!canceled) {
setState({ loading: false, data: null, error });
}
}
})();
return () => {
canceled = true;
};
}, dependencies);
return state;
}
// 使用
function UserProfile({ userId }) {
const { loading, data: user, error } = useAsync(
() => fetch(`/api/users/${userId}`).then(r => r.json()),
[userId]
);
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error.message}</div>;
if (!user) return null;
return <div>{user.name}</div>;
}
6.5 错误恢复与降级
class ResilientFetcher {
constructor(options = {}) {
this.retries = options.retries || 3;
this.timeout = options.timeout || 5000;
this.fallback = options.fallback;
}
async fetch(url, options = {}) {
// 尝试主请求
try {
return await this.fetchWithRetry(url, options);
} catch (primaryError) {
console.warn('主请求失败:', primaryError);
// 尝试降级方案
if (this.fallback) {
try {
console.log('尝试降级方案');
return await this.fallback(url, options);
} catch (fallbackError) {
console.error('降级方案也失败:', fallbackError);
}
}
throw primaryError;
}
}
async fetchWithRetry(url, options) {
let lastError;
for (let i = 0; i < this.retries; i++) {
try {
return await this.fetchWithTimeout(url, options);
} catch (error) {
lastError = error;
if (i < this.retries - 1) {
await this.delay(Math.pow(2, i) * 1000);
}
}
}
throw lastError;
}
async fetchWithTimeout(url, options) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} finally {
clearTimeout(timeoutId);
}
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// 使用
const fetcher = new ResilientFetcher({
retries: 3,
timeout: 5000,
fallback: async (url) => {
// 从缓存或备用服务器获取
return await fetchFromCache(url);
}
});
const data = await fetcher.fetch('/api/critical-data');
总结
核心要点
-
Generator 本质:
- 可暂停、可恢复的函数
- 基于状态机实现
- 通过
yield实现双向通信
-
async/await 本质:
- Generator + Promise 的语法糖
- 自动执行的 Generator
- 更优雅的异步编程方式
-
执行机制:
await会暂停函数执行,将后续代码放入微任务队列async函数总是返回 Promise- 错误通过 try-catch 或 Promise.catch 捕获
-
性能优化:
- 合理使用并发(Promise.all)
- 避免不必要的 await
- 使用 Promise.allSettled 处理部分失败
-
最佳实践:
- 不要在 forEach 中使用 await
- 使用 for…of 进行串行异步操作
- 合理设计错误处理策略
- 注意内存泄漏和取消操作
面试高频考点
- async/await 与 Promise 的关系
- Generator 执行机制和状态机原理
- 事件循环中 async/await 的执行顺序
- 并发控制和错误处理
- 手写 async/await 实现(基于 Generator)
进阶学习方向
- 深入理解事件循环机制
- 学习 RxJS 等响应式编程库
- 掌握 Web Worker 和多线程
- 了解 TC39 提案中的新异步特性
- 研究各框架的异步状态管理方案
参考资源: