深入理解 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 的暂停/恢复特性天然适合实现有限状态机
作用:
- 惰性求值:数据按需生成,处理大数据集时内存效率极高
- 简化异步:用同步的写法表达异步逻辑,消除回调嵌套
- 协程能力:函数可以在任意位置暂停,将控制权交还调用方
- 统一遍历协议:
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 3Async 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 的副作用管理 │
└──────────────────────────────────────────────────────────┘