js中的生成器函数
JavaScript 的生成器函数(Generator Function)是一种特殊的函数,它可以暂停执行和恢复执行,并可以产出(yield)多个值(而普通函数只能返回单个值)。
基本概念
1. 创建生成器函数
function* generatorFunction() {
yield '第一个值';
yield '第二个值';
yield '第三个值';
}
注意:使用 function* 声明(星号可以放在 function 后面或函数名前)
2. 基本使用
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
主要特性
1. 暂停和恢复执行
function* counter() {
console.log('开始');
yield '暂停点1';
console.log('继续执行');
yield '暂停点2';
return '结束';
}
const gen = counter();
console.log(gen.next()); // 开始 → { value: '暂停点1', done: false }
console.log(gen.next()); // 继续执行 → { value: '暂停点2', done: false }
console.log(gen.next()); // { value: '结束', done: true }
2. 双向通信
可以向生成器传递值:
function* twoWayCommunication() {
const name = yield '你的名字是什么?';
const age = yield `你好 ${name},你多大了?`;
return `${name} 今年 ${age} 岁`;
}
const gen = twoWayCommunication();
console.log(gen.next()); // { value: '你的名字是什么?', done: false }
console.log(gen.next('小明')); // { value: '你好 小明,你多大了?', done: false }
console.log(gen.next(25)); // { value: '小明 今年 25 岁', done: true }
3. 无限序列生成
function* infiniteNumbers() {
let n = 0;
while (true) {
yield n++;
}
}
const numbers = infiniteNumbers();
console.log(numbers.next().value); // 0
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// 可以无限调用...
实用场景
1. 惰性计算
function* take(n, generator) {
for (let i = 0; i < n; i++) {
yield generator.next().value;
}
}
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
// 只获取前10个斐波那契数
const firstTen = [...take(10, fibonacci())];
console.log(firstTen); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
2. 异步操作控制(async/await 出现前)
function asyncTask(time) {
return new Promise(resolve => setTimeout(() => resolve(`完成 ${time}ms`), time));
}
function* asyncGenerator() {
const result1 = yield asyncTask(1000);
console.log(result1);
const result2 = yield asyncTask(500);
console.log(result2);
}
// 手动执行生成器
function run(generator) {
const gen = generator();
function handle(result) {
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value)
.then(data => handle(gen.next(data)))
.catch(err => gen.throw(err));
}
return handle(gen.next());
}
run(asyncGenerator);
3. 状态机实现
function* stateMachine() {
let state = '开始';
while (true) {
const action = yield state;
if (action === '下一步' && state === '开始') {
state = '进行中';
} else if (action === '下一步' && state === '进行中') {
state = '完成';
} else if (action === '重置') {
state = '开始';
}
}
}
const machine = stateMachine();
console.log(machine.next().value); // '开始'
console.log(machine.next('下一步').value); // '进行中'
console.log(machine.next('下一步').value); // '完成'
console.log(machine.next('重置').value); // '开始'
4. 可迭代对象
生成器函数返回的是可迭代对象:
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
// 可以直接用于 for...of
for (let num of range(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
// 或者使用扩展运算符
const numbers = [...range(1, 3)];
console.log(numbers); // [1, 2, 3]
方法总结
生成器对象的方法:
function* sample() {
yield 1;
yield 2;
yield 3;
}
const gen = sample();
// 1. next() - 获取下一个值
gen.next();
// 2. return() - 结束生成器
gen.return('提前结束');
// 3. throw() - 向生成器抛出错误
gen.throw(new Error('出错了'));
注意事项
- 箭头函数不能作为生成器(箭头函数没有
function关键字) - 生成器函数调用时不立即执行,而是返回一个生成器对象
- 生成器对象既是迭代器也是可迭代对象
- 在类中也可以使用生成器方法:
class MyClass { *generatorMethod() { yield 'hello'; } }
异步生成器函数(Async Generator Functions)
异步生成器函数是生成器函数和异步函数的结合体,可以异步地产生一系列值。
基本语法
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
或使用简写形式:
async function* asyncGenerator() {
yield 1; // 自动包装为 Promise
yield 2;
yield 3;
}
基本使用
1. 创建和使用
async function* fetchInSequence() {
const data1 = await fetch('https://api.example.com/data1').then(r => r.json());
yield data1;
const data2 = await fetch('https://api.example.com/data2').then(r => r.json());
yield data2;
const data3 = await fetch('https://api.example.com/data3').then(r => r.json());
yield data3;
}
2. 遍历异步生成器
async function processAsyncGenerator() {
const gen = fetchInSequence();
for await (const data of gen) {
console.log(data);
// 每个数据会在上一个数据完全获取后才会开始获取下一个
}
}
主要特性
1. for await...of 循环
专门用于遍历异步迭代器:
async function* timer(seconds) {
for (let i = 1; i <= seconds; i++) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield `第 ${i} 秒`;
}
}
async function run() {
for await (const tick of timer(5)) {
console.log(tick);
}
console.log('计时结束');
}
// 输出:第 1 秒 → 第 2 秒 → ... → 第 5 秒 → 计时结束
2. 返回值是 AsyncGenerator 对象
async function* example() {
yield 1;
yield 2;
return 3; // return 的值在 done: true 时返回
}
async function run() {
const gen = example();
console.log(await gen.next()); // { value: 1, done: false }
console.log(await gen.next()); // { value: 2, done: false }
console.log(await gen.next()); // { value: 3, done: true }
}
实用场景
1. 分页数据获取
async function* paginatedData(url) {
let nextUrl = url;
while (nextUrl) {
const response = await fetch(nextUrl);
const data = await response.json();
yield data.results; // 假设 API 返回 { results: [...], next: 'url' }
nextUrl = data.next; // 下一页的 URL
}
}
// 使用
async function getAllData() {
const allData = [];
for await (const page of paginatedData('https://api.example.com/data?page=1')) {
allData.push(...page);
console.log(`已获取 ${allData.length} 条数据`);
}
return allData;
}
2. 实时数据流处理
async function* eventStream(eventSource) {
const controller = new AbortController();
const { signal } = controller;
try {
const response = await fetch(eventSource, { signal });
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const events = chunk.split('\n\n'); // SSE 格式
for (const event of events) {
if (event.trim()) {
yield JSON.parse(event.match(/{.*}/)?.[0] || '{}');
}
}
}
} finally {
controller.abort();
}
}
// 使用 SSE(Server-Sent Events)
async function handleRealTimeData() {
for await (const event of eventStream('/api/events')) {
updateUI(event);
}
}
3. 批量处理大型数据集
async function* batchProcessor(dataSource, batchSize = 100) {
let offset = 0;
let hasMore = true;
while (hasMore) {
const batch = await fetchBatch(dataSource, offset, batchSize);
if (batch.length === 0) {
hasMore = false;
} else {
// 处理当前批次
const processed = await processBatchAsync(batch);
yield processed;
offset += batchSize;
// 模拟延迟,避免服务器过载
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}
async function fetchBatch(source, offset, limit) {
// 实际的数据获取逻辑
return fetch(`${source}?offset=${offset}&limit=${limit}`)
.then(r => r.json());
}
4. 并发控制的任务队列
async function* taskRunner(tasks, concurrency = 3) {
const executing = new Set();
for (const task of tasks) {
const promise = task().then(result => {
executing.delete(promise);
return result;
});
executing.add(promise);
if (executing.size >= concurrency) {
// 等待至少一个任务完成
yield Promise.race(executing);
}
}
// 等待所有剩余任务完成
while (executing.size > 0) {
yield Promise.race(executing);
}
}
// 使用
async function runConcurrentTasks() {
const tasks = Array.from({ length: 10 }, (_, i) =>
() => new Promise(resolve =>
setTimeout(() => resolve(`任务 ${i + 1} 完成`), Math.random() * 1000)
)
);
const results = [];
for await (const result of taskRunner(tasks, 3)) {
results.push(result);
console.log(`已完成: ${result}, 剩余并发: ${results.length}`);
}
return results;
}
5. WebSocket 消息流
async function* webSocketStream(url, protocols = []) {
const ws = new WebSocket(url, protocols);
yield new Promise((resolve, reject) => {
ws.onopen = () => resolve('connected');
ws.onerror = reject;
});
try {
while (ws.readyState === WebSocket.OPEN) {
const message = await new Promise((resolve, reject) => {
ws.onmessage = (event) => resolve(event.data);
ws.onerror = reject;
ws.onclose = () => resolve(null);
});
if (message === null) break;
yield JSON.parse(message);
}
} finally {
if (ws.readyState === WebSocket.OPEN) {
ws.close();
}
}
}
// 使用
async function handleWebSocket() {
const stream = webSocketStream('wss://api.example.com/ws');
try {
console.log(await stream.next()); // 连接状态
for await (const message of stream) {
console.log('收到消息:', message);
// 处理消息...
}
} catch (error) {
console.error('WebSocket 错误:', error);
}
}
错误处理
async function* safeGenerator() {
try {
yield await fetchData();
yield await fetchMoreData();
} catch (error) {
console.error('生成器内部错误:', error);
yield { error: error.message };
} finally {
console.log('清理资源...');
// 清理逻辑
}
}
// 外部错误处理
async function handleErrors() {
const gen = safeGenerator();
try {
while (true) {
const { value, done } = await gen.next();
if (done) break;
if (value.error) {
// 处理错误结果
handleError(value.error);
} else {
// 处理正常结果
processData(value);
}
}
} catch (error) {
console.error('外部错误处理:', error);
}
}
组合使用
// 多个异步生成器组合
async function* mergedStreams(...streams) {
const promises = streams.map(async function* (stream) {
for await (const item of stream) {
yield item;
}
});
// 合并所有流
const allStreams = promises.flat();
for await (const item of allStreams) {
yield item;
}
}
// 过滤异步流
async function* filterAsync(stream, predicate) {
for await (const item of stream) {
if (await predicate(item)) {
yield item;
}
}
}
// 映射异步流
async function* mapAsync(stream, mapper) {
for await (const item of stream) {
yield await mapper(item);
}
}
注意事项
- 必须使用
for await...of或手动调用next()配合await - 返回的是 AsyncGenerator 对象,不是普通生成器
- 可以同时使用
await和yield - 错误传播:内部错误会向外抛出
- 资源清理:使用
try...finally确保资源释放
与普通生成器的比较
| 特性 | 普通生成器 | 异步生成器 |
|---|---|---|
| 声明 | function* |
async function* |
| 遍历 | for...of |
for await...of |
next() 返回 |
{ value, done } |
Promise<{ value, done }> |
| 可等待 | 否 | 是 |
| 主要用途 | 同步序列 | 异步序列/流 |
异步生成器函数在处理数据流、实时通信、分页数据等异步场景中非常有用,它们提供了一种简洁的方式来处理复杂的异步数据流。
生成器函数提供了强大的控制流管理能力,虽然在现代 JavaScript 中很多场景已被 async/await 替代,但在处理复杂的状态机、惰性计算、数据流处理等方面仍然非常有用。
浙公网安备 33010602011771号