迭代器与生成器
# 12.迭代器与生成器
迭代器(Iterator)和生成器(Generator)是 ES6 引入的核心特性,它们为 JavaScript 提供了统一的遍历接口和惰性求值能力。
for...of、展开运算符、解构赋值等语法糖的底层都依赖迭代器协议。
# 12.1 迭代协议
# 12.1.1 可迭代协议(Iterable Protocol)
一个对象要成为"可迭代的",必须实现 [Symbol.iterator]() 方法,该方法返回一个迭代器对象。
// 内置可迭代对象
// Array、String、Map、Set、TypedArray、arguments、NodeList
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
# 12.1.2 迭代器协议(Iterator Protocol)
迭代器必须实现 next() 方法,返回 { value, done } 格式的对象:
// 手动实现一个迭代器
function createRangeIterator(start, end) {
let current = start;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
const iter = createRangeIterator(1, 3);
iter.next(); // { value: 1, done: false }
iter.next(); // { value: 2, done: false }
iter.next(); // { value: 3, done: false }
iter.next(); // { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 12.1.3 自定义可迭代对象
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
},
// 可选:让迭代器本身也可迭代
[Symbol.iterator]() {
return this;
}
};
}
}
const range = new Range(1, 5);
// for...of
for (const n of range) {
console.log(n); // 1, 2, 3, 4, 5
}
// 展开运算符
console.log([...range]); // [1, 2, 3, 4, 5]
// 解构赋值
const [a, b, c] = range; // a=1, b=2, c=3
// Array.from
Array.from(range); // [1, 2, 3, 4, 5]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 12.1.4 迭代器的消费者
以下语法结构会自动调用迭代器协议:
| 消费者 | 说明 |
|---|---|
for...of | 循环遍历 |
...spread | 展开运算符 |
[a, b] = iterable | 解构赋值 |
Array.from() | 转换为数组 |
new Map() / new Set() | 构造集合 |
Promise.all() / Promise.race() | 接受可迭代对象 |
yield* | 委托生成器 |
# 12.1.5 提前终止:return() 和 throw()
迭代器可以选择实现 return() 和 throw() 方法,用于资源清理:
class FileLines {
constructor(filename) {
this.filename = filename;
}
[Symbol.iterator]() {
let lineNo = 0;
const lines = ['line1', 'line2', 'line3']; // 模拟文件行
return {
next() {
if (lineNo < lines.length) {
return { value: lines[lineNo++], done: false };
}
return { value: undefined, done: true };
},
// for...of 中 break 时调用
return() {
console.log('Iterator closed, releasing resources');
return { value: undefined, done: true };
}
};
}
}
const file = new FileLines('test.txt');
for (const line of file) {
console.log(line);
if (line === 'line1') break; // 触发 return()
}
// 输出: line1, Iterator closed, releasing resources
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 12.2 生成器函数
# 12.2.1 基本语法
生成器函数使用 function* 声明,内部使用 yield 关键字暂停执行:
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 }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
关键概念:
- 调用生成器函数不执行函数体,返回一个生成器对象
- 每次调用
next()执行到下一个yield暂停 yield表达式的值就是next()返回对象中的value- 函数执行完毕后
done变为true
# 12.2.2 yield 的双向通信
yield 不仅能输出值,还能接收值。next(value) 中传入的参数会成为上一个 yield 表达式的返回值:
function* dialog() {
const name = yield '你叫什么名字?';
const age = yield `${name},你多大了?`;
return `${name} 今年 ${age} 岁`;
}
const gen = dialog();
console.log(gen.next()); // { value: '你叫什么名字?', done: false }
console.log(gen.next('Alice')); // { value: 'Alice,你多大了?', done: false }
console.log(gen.next(25)); // { value: 'Alice 今年 25 岁', done: true }
2
3
4
5
6
7
8
9
10
执行流程解析:
第1次 next(): 执行到第一个 yield,输出 '你叫什么名字?'
第2次 next('Alice'): 'Alice' 赋值给 name,执行到第二个 yield
第3次 next(25): 25 赋值给 age,执行 return
2
3
注意:第一次
next()的参数会被忽略,因为此时没有待接收值的yield。
# 12.2.3 return() 和 throw()
function* gen() {
try {
yield 1;
yield 2;
yield 3;
} catch (e) {
console.log('捕获错误:', e.message);
} finally {
console.log('清理资源');
}
}
// return() - 提前终止生成器
const g1 = gen();
g1.next(); // { value: 1, done: false }
g1.return('结束'); // '清理资源' { value: '结束', done: true }
g1.next(); // { value: undefined, done: true }
// throw() - 向生成器内部抛出错误
const g2 = gen();
g2.next(); // { value: 1, done: false }
g2.throw(new Error('出错了'));
// '捕获错误: 出错了'
// '清理资源'
// { value: undefined, done: true }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 12.2.4 yield* 委托
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);
yield 2;
}
console.log([...outer()]);
// inner 返回: inner done
// [1, 'a', 'b', 2]
// 注意:inner 的 return 值不在遍历结果中
// yield* 可以委托给任何可迭代对象
function* chars(str) {
yield* str; // 字符串是可迭代的
}
console.log([...chars('hello')]); // ['h', 'e', 'l', 'l', 'o']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
yield 实现树的深度优先遍历*:
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child; // 递归委托
}
}
}
const tree = new TreeNode('root', [
new TreeNode('A', [
new TreeNode('A1'),
new TreeNode('A2'),
]),
new TreeNode('B', [
new TreeNode('B1'),
]),
]);
console.log([...tree]); // ['root', 'A', 'A1', 'A2', 'B', 'B1']
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 12.3 生成器的实用模式
# 12.3.1 无限序列
生成器可以表示无限序列,只在需要时计算值(惰性求值):
// 自然数
function* naturals(start = 1) {
let n = start;
while (true) {
yield n++;
}
}
// 斐波那契数列
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// 取前 10 个斐波那契数
function take(n, iterable) {
const result = [];
for (const value of iterable) {
result.push(value);
if (result.length >= n) break;
}
return result;
}
console.log(take(10, fibonacci()));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 12.3.2 惰性管道
将多个转换操作组合成惰性求值管道,避免创建中间数组:
function* map(iterable, fn) {
for (const value of iterable) {
yield fn(value);
}
}
function* filter(iterable, pred) {
for (const value of iterable) {
if (pred(value)) yield value;
}
}
function* takeWhile(iterable, pred) {
for (const value of iterable) {
if (!pred(value)) return;
yield value;
}
}
// 找出 1000 以内所有斐波那契偶数
const result = [
...filter(
takeWhile(fibonacci(), n => n < 1000),
n => n % 2 === 0
)
];
console.log(result); // [0, 2, 8, 34, 144, 610]
// 没有创建任何中间数组!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 12.3.3 状态机
生成器天然适合表达状态机,每个 yield 是一个状态:
function* trafficLight() {
while (true) {
yield '🔴 红灯 - 停止';
yield '🟢 绿灯 - 通行';
yield '🟡 黄灯 - 减速';
}
}
const light = trafficLight();
light.next().value; // '🔴 红灯 - 停止'
light.next().value; // '🟢 绿灯 - 通行'
light.next().value; // '🟡 黄灯 - 减速'
light.next().value; // '🔴 红灯 - 停止'(循环)
2
3
4
5
6
7
8
9
10
11
12
13
# 12.3.4 协程(Coroutine)
生成器可以实现协程模式,多个执行流交替执行:
function* taskA() {
console.log('A: 步骤 1');
yield;
console.log('A: 步骤 2');
yield;
console.log('A: 步骤 3');
}
function* taskB() {
console.log('B: 步骤 1');
yield;
console.log('B: 步骤 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([taskA, taskB]);
// A: 步骤 1
// B: 步骤 1
// A: 步骤 2
// B: 步骤 2
// A: 步骤 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 12.3.5 生成器实现 async/await
async/await 的底层原理就是生成器 + Promise 的自动执行器:
// async/await 的等价实现
function spawn(generatorFn) {
return function(...args) {
const gen = generatorFn.apply(this, args);
return new Promise((resolve, reject) => {
function step(method, arg) {
let result;
try {
result = gen[method](arg);
} catch (e) {
return reject(e);
}
if (result.done) {
return resolve(result.value);
}
Promise.resolve(result.value).then(
value => step('next', value),
err => step('throw', err)
);
}
step('next', undefined);
});
};
}
// 用生成器写异步代码
const fetchUser = spawn(function* (id) {
const response = yield fetch(`/api/users/${id}`);
const data = yield response.json();
return data;
});
// 等价于
async function fetchUserAsync(id) {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
return data;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 12.4 异步迭代器
# 12.4.1 异步迭代协议
ES2018 引入了异步迭代协议,next() 方法返回 Promise:
// 异步可迭代对象实现 [Symbol.asyncIterator]()
const asyncRange = {
from: 1,
to: 5,
[Symbol.asyncIterator]() {
let current = this.from;
const last = this.to;
return {
async next() {
// 模拟异步操作
await new Promise(r => setTimeout(r, 100));
if (current <= last) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
// 用 for-await-of 遍历
async function main() {
for await (const num of asyncRange) {
console.log(num); // 1, 2, 3, 4, 5(每个间隔 100ms)
}
}
main();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 12.4.2 异步生成器
异步生成器结合了 async 和 function*:
async function* fetchPages(baseUrl, totalPages) {
for (let page = 1; page <= totalPages; page++) {
const response = await fetch(`${baseUrl}?page=${page}`);
const data = await response.json();
yield data;
}
}
// 使用
async function processAllPages() {
for await (const pageData of fetchPages('/api/items', 5)) {
console.log('Processing page:', pageData);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 12.4.3 流式数据处理
异步迭代器非常适合处理流式数据:
// 读取 ReadableStream
async function* readStream(stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
} finally {
reader.releaseLock();
}
}
// 逐行读取文本流
async function* readLines(stream) {
let buffer = '';
const decoder = new TextDecoder();
for await (const chunk of readStream(stream)) {
buffer += decoder.decode(chunk, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop(); // 最后一段可能不完整
for (const line of lines) {
yield line;
}
}
if (buffer) yield buffer; // 最后的剩余
}
// SSE (Server-Sent Events) 处理
async function* parseSSE(response) {
for await (const line of readLines(response.body)) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
yield JSON.parse(data);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 12.4.4 并发控制
结合异步迭代器实现并发控制:
async function* asyncPool(concurrency, iterable, fn) {
const executing = new Set();
for (const item of iterable) {
const promise = fn(item).then(result => {
executing.delete(promise);
return result;
});
executing.add(promise);
if (executing.size >= concurrency) {
yield await Promise.race(executing);
}
}
while (executing.size > 0) {
yield await Promise.race(executing);
}
}
// 使用:同时最多 3 个并发请求
const urls = ['/api/1', '/api/2', '/api/3', '/api/4', '/api/5'];
async function main() {
for await (const result of asyncPool(3, urls, url => fetch(url))) {
console.log('Got response:', result.status);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 12.5 内置可迭代对象详解
# 12.5.1 Array 迭代器
const arr = ['a', 'b', 'c'];
// values() - 值迭代器(默认)
for (const val of arr.values()) { /* 'a', 'b', 'c' */ }
// keys() - 键迭代器
for (const key of arr.keys()) { /* 0, 1, 2 */ }
// entries() - 键值对迭代器
for (const [i, val] of arr.entries()) {
console.log(i, val); // 0 'a', 1 'b', 2 'c'
}
// arr[Symbol.iterator] === arr.values
console.log(arr[Symbol.iterator] === arr.values); // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 12.5.2 Map 和 Set 迭代器
const map = new Map([['a', 1], ['b', 2]]);
// Map 默认迭代 entries
for (const [key, value] of map) {
console.log(key, value); // 'a' 1, 'b' 2
}
// Map 的迭代顺序 = 插入顺序
map.keys(); // MapIterator {'a', 'b'}
map.values(); // MapIterator {1, 2}
map.entries(); // MapIterator {['a', 1], ['b', 2]}
const set = new Set([1, 2, 3]);
// Set 的 values() 和 keys() 相同
for (const val of set) {
console.log(val); // 1, 2, 3
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 12.5.3 String 迭代器
const str = 'A😀B';
// for...of 正确处理 Unicode
for (const char of str) {
console.log(char); // 'A', '😀', 'B'(3次,非4次)
}
// 对比 for 循环
for (let i = 0; i < str.length; i++) {
console.log(str[i]); // 'A', '�', '�', 'B'(4次,emoji 被拆开)
}
2
3
4
5
6
7
8
9
10
11
# 12.5.4 arguments 和 NodeList
// arguments 是可迭代的
function test() {
for (const arg of arguments) {
console.log(arg);
}
}
// NodeList 也是可迭代的
const divs = document.querySelectorAll('div');
for (const div of divs) {
console.log(div.textContent);
}
2
3
4
5
6
7
8
9
10
11
12
# 12.6 迭代器工具库
实际开发中常用的迭代器工具函数:
// 组合多个可迭代对象
function* chain(...iterables) {
for (const iterable of iterables) {
yield* iterable;
}
}
console.log([...chain([1, 2], [3, 4], [5])]); // [1, 2, 3, 4, 5]
// 压缩两个可迭代对象
function* zip(...iterables) {
const iterators = iterables.map(i => i[Symbol.iterator]());
while (true) {
const results = iterators.map(it => it.next());
if (results.some(r => r.done)) return;
yield results.map(r => r.value);
}
}
console.log([...zip([1, 2, 3], ['a', 'b', 'c'])]);
// [[1, 'a'], [2, 'b'], [3, 'c']]
// 枚举(带索引)
function* enumerate(iterable, start = 0) {
let i = start;
for (const value of iterable) {
yield [i++, value];
}
}
for (const [i, v] of enumerate(['a', 'b', 'c'])) {
console.log(i, v); // 0 'a', 1 'b', 2 'c'
}
// 分块
function* chunk(iterable, size) {
let batch = [];
for (const item of iterable) {
batch.push(item);
if (batch.length === size) {
yield batch;
batch = [];
}
}
if (batch.length > 0) yield batch;
}
console.log([...chunk([1,2,3,4,5], 2)]);
// [[1,2], [3,4], [5]]
// 扁平化
function* flatten(iterable, depth = 1) {
for (const item of iterable) {
if (depth > 0 && item[Symbol.iterator] && typeof item !== 'string') {
yield* flatten(item, depth - 1);
} else {
yield item;
}
}
}
console.log([...flatten([[1, [2]], [3, [4, [5]]]], Infinity)]);
// [1, 2, 3, 4, 5]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 12.7 生成器与迭代器对比
| 特性 | 手动迭代器 | 生成器 |
|---|---|---|
| 语法复杂度 | 高(手动管理状态) | 低(yield 自动管理) |
| 可读性 | 较差 | 好(线性代码流) |
return() 支持 | 需手动实现 | 自动支持(通过 finally) |
| 内存效率 | 相同 | 相同(都是惰性求值) |
| 双向通信 | 不支持 | 支持(next(value)) |
| 委托 | 手动实现 | yield* 语法 |
| 调试 | 困难 | 可在 yield 处断点 |
建议:大多数场景优先使用生成器,除非需要极致的性能优化或在不支持生成器的环境中。