Skip to content

深入理解 Generator 与 Iterator

迭代协议、Generator 完整用法、async generator、co 库原理与 redux-saga 基础

什么是 Generator 与 Iterator?

定义Iterator(迭代器)是一种统一的遍历接口,任何实现了 next() 方法并返回 { value, done } 的对象都是迭代器。Generator(生成器)是 ES6 引入的特殊函数(function*),调用后返回一个迭代器对象,函数体内通过 yield 关键字可以暂停和恢复执行,实现惰性求值。

涉及场景

  • 自定义迭代:让任意对象支持 for...of、展开运算符、解构赋值
  • 惰性序列:按需生成无限序列(斐波那契、ID生成器),不占用额外内存
  • 异步流程控制co 库 / redux-saga 用 Generator 管理复杂异步流程
  • async/await 原理async 函数本质上是 Generator + 自动执行器的语法糖
  • 数据流处理async generator + for await...of 处理流式数据(SSE、分页加载)
  • 状态机:Generator 的暂停/恢复特性天然适合实现有限状态机

作用

  1. 惰性求值:数据按需生成,处理大数据集时内存效率极高
  2. 简化异步:用同步的写法表达异步逻辑,消除回调嵌套
  3. 协程能力:函数可以在任意位置暂停,将控制权交还调用方
  4. 统一遍历协议Symbol.iterator 让所有可迭代对象具有一致的遍历方式

迭代协议(Iteration Protocol)

ES6 定义了两个协议:可迭代协议迭代器协议

可迭代协议(Iterable Protocol)

对象实现 [Symbol.iterator]() 方法,返回一个迭代器。

javascript
// 内置可迭代对象:Array、String、Map、Set、arguments、NodeList、TypedArray

// 可迭代对象可以用于:
for (const item of iterable) {}          // for...of
const arr = [...iterable];               // 展开运算符
const [a, b] = iterable;                // 解构赋值
Array.from(iterable);                    // Array.from
new Map(iterable);                       // Map/Set 构造函数
new Set(iterable);
Promise.all(iterable);                   // Promise 方法
yield* iterable;                         // yield 委托

迭代器协议(Iterator Protocol)

对象实现 next() 方法,返回 { value, done }

javascript
// 手动实现一个可迭代对象
class Range {
  constructor(start, end, step = 1) {
    this.start = start;
    this.end = end;
    this.step = step;
  }

  [Symbol.iterator]() {
    let current = this.start;
    const { end, step } = this;

    return {
      next() {
        if (current <= end) {
          const value = current;
          current += step;
          return { value, done: false };
        }
        return { done: true, value: undefined };
      },

      // 可选:让迭代器本身也是可迭代的
      [Symbol.iterator]() {
        return this;
      }
    };
  }
}

const range = new Range(1, 10, 2);
for (const n of range) {
  console.log(n); // 1, 3, 5, 7, 9
}
console.log([...range]); // [1, 3, 5, 7, 9]

// 迭代器的 return() 方法(可选)
// 当 for...of 提前退出时(break/throw/return)调用
const iterableWithCleanup = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        i++;
        return { value: i, done: i > 10 };
      },
      return() {
        console.log('迭代器被提前关闭,执行清理');
        return { done: true };
      }
    };
  }
};

for (const n of iterableWithCleanup) {
  if (n > 3) break; // 触发 return()
  console.log(n);
}
// 1, 2, 3, '迭代器被提前关闭,执行清理'

Generator 函数

Generator 是一种特殊函数,可以暂停和恢复执行。用 function* 声明,内部用 yield 暂停。

javascript
function* simpleGenerator() {
  console.log('开始');
  yield 1;
  console.log('第一次恢复');
  yield 2;
  console.log('第二次恢复');
  yield 3;
  console.log('结束');
}

const gen = simpleGenerator(); // 不执行函数体!返回迭代器

gen.next(); // '开始' → { value: 1, done: false }
gen.next(); // '第一次恢复' → { value: 2, done: false }
gen.next(); // '第二次恢复' → { value: 3, done: false }
gen.next(); // '结束' → { value: undefined, done: true }

// Generator 自动实现了可迭代协议
for (const n of simpleGenerator()) {
  console.log(n); // 1, 2, 3
}

双向通信:next() 传值

javascript
function* calculator() {
  const a = yield '请输入第一个数';
  const b = yield '请输入第二个数';
  const op = yield '请输入运算符 (+, -, *, /)';

  let result;
  switch (op) {
    case '+': result = a + b; break;
    case '-': result = a - b; break;
    case '*': result = a * b; break;
    case '/': result = a / b; break;
  }

  return `${a} ${op} ${b} = ${result}`;
}

const calc = calculator();
calc.next();        // { value: '请输入第一个数', done: false }
calc.next(10);      // { value: '请输入第二个数', done: false }  ← 10 赋值给 a
calc.next(5);       // { value: '请输入运算符', done: false }   ← 5 赋值给 b
calc.next('+');     // { value: '10 + 5 = 15', done: true }   ← '+' 赋值给 op

// ⚠️ 第一次 next() 的参数会被忽略(没有 yield 来接收)

throw() 和 return()

javascript
function* errorDemo() {
  try {
    const a = yield 1;
    const b = yield 2;
    return a + b;
  } catch (e) {
    console.log('捕获错误:', e.message);
    yield '错误已处理';
  } finally {
    console.log('finally 执行');
  }
}

// throw() - 向 Generator 内部抛出错误
const gen1 = errorDemo();
gen1.next();           // { value: 1, done: false }
gen1.throw(new Error('出错了'));
// 捕获错误: 出错了 → { value: '错误已处理', done: false }

// return() - 提前终止 Generator
const gen2 = errorDemo();
gen2.next();           // { value: 1, done: false }
gen2.return('终止');
// finally 执行 → { value: '终止', done: true }
// 注意:finally 块仍然会执行!

yield* 委托

javascript
// yield* 将迭代委托给另一个可迭代对象
function* inner() {
  yield 'a';
  yield 'b';
  return 'inner done'; // return 的值作为 yield* 的结果
}

function* outer() {
  yield 1;
  const result = yield* inner(); // 委托给 inner
  console.log('inner 返回:', result); // 'inner done'
  yield 2;
}

console.log([...outer()]); // [1, 'a', 'b', 2]
// 注意:inner 的 return 值不会出现在迭代结果中

// yield* 可以委托给任何可迭代对象
function* flatten(arr) {
  for (const item of arr) {
    if (Array.isArray(item)) {
      yield* flatten(item); // 递归委托
    } else {
      yield item;
    }
  }
}

console.log([...flatten([1, [2, [3, 4], 5], 6])]); // [1, 2, 3, 4, 5, 6]

// 用 yield* 实现二叉树中序遍历
class TreeNode {
  constructor(value, left, right) {
    this.value = value;
    this.left = left;
    this.right = right;
  }

  *[Symbol.iterator]() {
    if (this.left) yield* this.left;
    yield this.value;
    if (this.right) yield* this.right;
  }
}

const tree = new TreeNode(4,
  new TreeNode(2, new TreeNode(1), new TreeNode(3)),
  new TreeNode(6, new TreeNode(5), new TreeNode(7))
);

console.log([...tree]); // [1, 2, 3, 4, 5, 6, 7]

实用模式

无限序列

javascript
function* naturals(start = 1) {
  let n = start;
  while (true) {
    yield n++;
  }
}

// 惰性求值 - 只在需要时计算
function* take(iterable, n) {
  let count = 0;
  for (const item of iterable) {
    if (count >= n) return;
    yield item;
    count++;
  }
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) yield item;
  }
}

function* map(iterable, transform) {
  for (const item of iterable) {
    yield transform(item);
  }
}

// 组合:取前10个偶数的平方
const result = [...take(
  map(
    filter(naturals(), n => n % 2 === 0),
    n => n * n
  ),
  10
)];
console.log(result); // [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

状态机

javascript
function* trafficLight() {
  while (true) {
    yield 'green';
    yield 'yellow';
    yield 'red';
  }
}

const light = trafficLight();
light.next().value; // 'green'
light.next().value; // 'yellow'
light.next().value; // 'red'
light.next().value; // 'green'(循环)

协程(Coroutine)

javascript
// Generator 实现简单的协程调度
function* task1() {
  console.log('Task1: step 1');
  yield;
  console.log('Task1: step 2');
  yield;
  console.log('Task1: step 3');
}

function* task2() {
  console.log('Task2: step 1');
  yield;
  console.log('Task2: step 2');
}

function scheduler(tasks) {
  const gens = tasks.map(t => t());

  while (gens.length > 0) {
    const gen = gens.shift();
    const { done } = gen.next();
    if (!done) {
      gens.push(gen); // 未完成的放回队列
    }
  }
}

scheduler([task1, task2]);
// Task1: step 1
// Task2: step 1
// Task1: step 2
// Task2: step 2
// Task1: step 3

Async Generator(异步生成器)

javascript
// async function* —— 结合 async 和 generator
async function* fetchPages(url) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(`${url}?page=${page}&limit=10`);
    const data = await response.json();

    if (data.items.length === 0) {
      hasMore = false;
    } else {
      yield data.items; // 每次 yield 一页数据
      page++;
    }
  }
}

// 使用 for await...of 消费
async function processAllPages() {
  for await (const items of fetchPages('/api/products')) {
    for (const item of items) {
      console.log(item.name);
    }
  }
}

// 实时数据流
async function* eventStream(url) {
  const eventSource = new EventSource(url);
  const queue = [];
  let resolve;

  eventSource.onmessage = (event) => {
    if (resolve) {
      resolve(JSON.parse(event.data));
      resolve = null;
    } else {
      queue.push(JSON.parse(event.data));
    }
  };

  try {
    while (true) {
      if (queue.length > 0) {
        yield queue.shift();
      } else {
        yield await new Promise(r => { resolve = r; });
      }
    }
  } finally {
    eventSource.close(); // 清理资源
  }
}

// 逐行读取文件(Node.js)
async function* readLines(filePath) {
  const fileStream = fs.createReadStream(filePath);
  const rl = readline.createInterface({ input: fileStream });

  for await (const line of rl) {
    yield line;
  }
}

co 库原理(Generator 自动执行)

javascript
// co 库让 Generator 可以像 async/await 一样使用
// 这就是 async/await 的前身

function co(generatorFn) {
  return new Promise((resolve, reject) => {
    const gen = generatorFn();

    function step(nextFn) {
      let result;
      try {
        result = nextFn();
      } catch (e) {
        return reject(e);
      }

      if (result.done) {
        return resolve(result.value);
      }

      // 将 yield 的值包装为 Promise
      Promise.resolve(result.value).then(
        (value) => step(() => gen.next(value)),
        (err) => step(() => gen.throw(err))
      );
    }

    step(() => gen.next());
  });
}

// 使用 co(ES6时代的异步方案)
co(function* () {
  const user = yield fetch('/api/user').then(r => r.json());
  const posts = yield fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
  return posts;
}).then(posts => console.log(posts));

// 等价的 async/await(现代方案)
async function getPosts() {
  const user = await fetch('/api/user').then(r => r.json());
  const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
  return posts;
}

总结

Generator & Iterator 核心知识点:
┌──────────────────────────────────────────────────────────┐
│ Iterator 协议                                             │
│ • [Symbol.iterator]() 返回迭代器                           │
│ • next() 返回 { value, done }                             │
│ • 支持 for...of、展开、解构等语法                            │
├──────────────────────────────────────────────────────────┤
│ Generator 函数                                            │
│ • function* 声明,yield 暂停                               │
│ • next(value) 双向通信                                    │
│ • throw(error) 向内部抛错                                  │
│ • return(value) 提前终止                                   │
│ • yield* 委托给其他可迭代对象                               │
├──────────────────────────────────────────────────────────┤
│ Async Generator                                           │
│ • async function* 声明                                    │
│ • for await...of 消费                                     │
│ • 适合分页加载、数据流、实时事件                              │
├──────────────────────────────────────────────────────────┤
│ 实际应用                                                   │
│ • 惰性序列(无限序列 + take/filter/map)                    │
│ • 状态机、协程调度                                          │
│ • co 库 = async/await 的前身                               │
│ • redux-saga 基于 Generator 的副作用管理                    │
└──────────────────────────────────────────────────────────┘