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('出错了'));

注意事项

  1. 箭头函数不能作为生成器(箭头函数没有 function 关键字)
  2. 生成器函数调用时不立即执行,而是返回一个生成器对象
  3. 生成器对象既是迭代器也是可迭代对象
  4. 在类中也可以使用生成器方法:
    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);
  }
}

注意事项

  1. 必须使用 for await...of 或手动调用 next() 配合 await
  2. 返回的是 AsyncGenerator 对象,不是普通生成器
  3. 可以同时使用 awaityield
  4. 错误传播:内部错误会向外抛出
  5. 资源清理:使用 try...finally 确保资源释放

与普通生成器的比较

特性 普通生成器 异步生成器
声明 function* async function*
遍历 for...of for await...of
next() 返回 { value, done } Promise<{ value, done }>
可等待
主要用途 同步序列 异步序列/流

异步生成器函数在处理数据流、实时通信、分页数据等异步场景中非常有用,它们提供了一种简洁的方式来处理复杂的异步数据流。
生成器函数提供了强大的控制流管理能力,虽然在现代 JavaScript 中很多场景已被 async/await 替代,但在处理复杂的状态机、惰性计算、数据流处理等方面仍然非常有用。

posted @ 2026-02-06 20:02  悠哉大斌  阅读(10)  评论(0)    收藏  举报