迭代器与生成器
What — 是什么
迭代器(Iterator)和生成器(Generator)是 JavaScript 中处理数据序列的核心机制。迭代器通过统一协议让任何对象可遍历,生成器通过
function*和yield以极简语法创建迭代器,并支持惰性求值、无限序列和异步流程控制。
核心概念:
- 可迭代协议(Iterable Protocol):对象实现
[Symbol.iterator]()方法,返回一个迭代器 - 迭代器协议(Iterator Protocol):对象实现
next()方法,返回{ value, done } - 生成器函数:
function*声明,内部用yield暂停/恢复执行,调用后返回生成器对象 - yield/yield*:
yield暂停并产出值,yield*委托给另一个可迭代对象 - 生成器是迭代器的语法糖:生成器对象同时实现迭代器协议和可迭代协议
关键特性:
- 迭代器是”拉取式”数据消费:消费者主动调用
next()获取下一个值 - 生成器函数体在每次
yield处暂停,next()恢复执行 next(arg)的参数会作为上一个yield表达式的返回值- 生成器对象还有
return()和throw()方法用于外部控制 - 异步生成器
async function*配合for await...of处理异步数据流
Why — 为什么
适用场景:
- 自定义数据结构的遍历方式(链表、树、图)
- 惰性求值——按需生成数据,不一次性加载到内存
- 无限序列(斐波那契、ID 生成器、数据流)
- 异步流程控制(生成器 + Promise 是 async/await 的前身)
- 数据管道与转换(链式处理,中间件模式)
- 状态机实现(用生成器替代显式状态变量)
对比替代方案:
| 维度 | 生成器 | 手写迭代器 | 数组/普通集合 | async/await |
|---|---|---|---|---|
| 代码量 | 极少 | 多 | 少 | 少 |
| 惰性求值 | 原生支持 | 需手动 | 不支持(全量加载) | 不适用 |
| 无限序列 | 支持 | 需手动 | 不可能 | 不适用 |
| 异步控制 | 需配合 co 库 | 不适用 | 不适用 | 原生语法 |
| 调试体验 | 中等 | 较好 | 极好 | 极好 |
优缺点:
- ✅ 优点:
- 极简语法创建迭代器,无需手写
next()逻辑 - 惰性求值节省内存,支持无限序列
yield*委托让组合与复用变得自然- 异步生成器统一同步/异步迭代模式
- 状态机替代方案,代码可读性高
- 极简语法创建迭代器,无需手写
- ❌ 缺点:
- 生成器不可复用——一次消费后需重新创建
- 调试困难(调用栈不直观,难以追踪暂停点)
- 异步流程控制已被 async/await 取代,生成器做异步属历史用法
- 浏览器兼容性好但心智模型需要适应
How — 怎么用
快速上手
// 1. 最简单的生成器
function* greet() {
yield 'Hello';
yield 'World';
}
const g = greet();
console.log(g.next()); // { value: 'Hello', done: false }
console.log(g.next()); // { value: 'World', done: false }
console.log(g.next()); // { value: undefined, done: true }
// 2. 用 for...of 消费生成器
function* range(start, end) {
for (let i = start; i <= end; i++) yield i;
}
for (const n of range(1, 5)) {
console.log(n); // 1 2 3 4 5
}
// 3. 手写迭代器(对比生成器的简洁性)
const rangeManual = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { done: true };
},
};
},
};
for (const n of rangeManual) console.log(n); // 1 2 3 4 5
代码示例
1. 手写迭代器
range 迭代器:
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { done: true };
},
};
},
};
// 消费
console.log([...range]); // [1, 2, 3, 4, 5]
链表迭代器:
class ListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
}
append(value) {
const node = new ListNode(value);
if (!this.head) {
this.head = this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
return this;
}
// 实现 Symbol.iterator 使链表可遍历
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
const value = current.value;
current = current.next;
return { value, done: false };
}
return { done: true };
},
};
}
}
const list = new LinkedList();
list.append(10).append(20).append(30);
for (const val of list) console.log(val); // 10 20 30
console.log([...list]); // [10, 20, 30]
2. 生成器基础
yield 表达式有返回值:
function* dialog() {
const name = yield 'What is your name?';
const age = yield `Hello, ${name}! How old are you?`;
return `${name}, age ${age}, welcome!`;
}
const g = dialog();
console.log(g.next()); // { value: 'What is your name?', done: false }
console.log(g.next('Alice')); // { value: 'Hello, Alice! How old are you?', done: false }
console.log(g.next(25)); // { value: 'Alice, age 25, welcome!', done: true }
next 传参机制详解:
function* accumulator() {
let sum = 0;
while (true) {
// next(n) 传入的 n 作为 yield 表达式的值
const input = yield sum;
if (input === null) break; // 传入 null 停止
sum += input;
}
return sum;
}
const acc = accumulator();
acc.next(); // 启动生成器,{ value: 0, done: false }
acc.next(10); // { value: 10, done: false }
acc.next(20); // { value: 30, done: false }
acc.next(5); // { value: 35, done: false }
acc.next(null); // { value: 35, done: true }
return 提前结束:
function* numbers() {
yield 1;
yield 2;
return 'done'; // 提前结束,返回值出现在最终 next 的 value 中
yield 3; // 永远不会执行
}
const g = numbers();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 'done', done: true }
// return() 方法:从外部强制终止
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g2 = gen();
console.log(g2.next()); // { value: 1, done: false }
console.log(g2.return('stop')); // { value: 'stop', done: true }
console.log(g2.next()); // { value: undefined, done: true }
3. yield* 委托生成器
基础委托:
function* inner() {
yield 'a';
yield 'b';
}
function* outer() {
yield 1;
yield* inner(); // 委托给 inner 生成器
yield 3;
}
console.log([...outer()]); // [1, 'a', 'b', 3]
// yield* 的返回值是被委托生成器的 return 值
function* sub() {
yield 'x';
return 'sub-done';
}
function* main() {
const result = yield* sub();
console.log('委托返回:', result); // 'sub-done'
yield result;
}
console.log([...main()]); // 委托返回: sub-done → ['x', 'sub-done']
树遍历:
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
}
// 深度优先遍历 — yield* 委托让递归极其简洁
function* dfs(node) {
yield node.value;
for (const child of node.children) {
yield* dfs(child); // 递归委托
}
}
const tree = new TreeNode('root', [
new TreeNode('a', [
new TreeNode('a1'),
new TreeNode('a2'),
]),
new TreeNode('b', [
new TreeNode('b1'),
]),
]);
console.log([...dfs(tree)]); // ['root', 'a', 'a1', 'a2', 'b', 'b1']
嵌套迭代——展开多维结构:
function* flatten(arr) {
for (const item of arr) {
if (Array.isArray(item)) {
yield* flatten(item); // 递归委托
} else {
yield item;
}
}
}
const nested = [1, [2, 3], [4, [5, 6]], 7];
console.log([...flatten(nested)]); // [1, 2, 3, 4, 5, 6, 7]
4. 生成器实现异步流程控制
手动 co 库——生成器自动执行器:
// 自动执行生成器中的 Promise,是 async/await 的前身
function co(generatorFn) {
const gen = generatorFn();
function step(val) {
const { value, done } = gen.next(val);
if (done) return Promise.resolve(value);
// value 是 Promise,等它 resolve 后继续推进
return Promise.resolve(value).then(
(res) => step(res),
(err) => gen.throw(err) // 错误抛回生成器
);
}
return step();
}
// 使用 — 和 async/await 几乎一样的体验
function fetchUser(id) {
return new Promise((resolve) =>
setTimeout(() => resolve({ id, name: 'Alice' }), 100)
);
}
function fetchPosts(userId) {
return new Promise((resolve) =>
setTimeout(() => resolve([`post1-by-${userId}`, 'post2']), 100)
);
}
co(function* () {
const user = yield fetchUser(1);
console.log(user); // { id: 1, name: 'Alice' }
const posts = yield fetchPosts(user.id);
console.log(posts); // ['post1-by-1', 'post2']
return posts;
});
async/await 的底层原理:
// async/await 本质是生成器 + 自动执行器的语法糖
// 以下两段代码等价:
// --- async/await 写法 ---
async function fetchAndProcess() {
try {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
return posts;
} catch (err) {
console.error(err);
}
}
// --- 生成器 + co 写法 ---
function* fetchAndProcessGen() {
try {
const user = yield fetchUser(1);
const posts = yield fetchPosts(user.id);
return posts;
} catch (err) {
console.error(err);
}
}
co(fetchAndProcessGen);
5. 异步迭代器与异步生成器
Symbol.asyncIterator 与 for await…of:
// 异步可迭代对象:实现 [Symbol.asyncIterator]() 返回 { next() → Promise<{value, done}> }
const asyncRange = {
from: 1,
to: 5,
[Symbol.asyncIterator]() {
let current = this.from;
const last = this.to;
return {
next() {
// next() 返回 Promise
return new Promise((resolve) => {
setTimeout(() => {
if (current <= last) {
resolve({ value: current++, done: false });
} else {
resolve({ done: true });
}
}, 100);
});
},
};
},
};
// 用 for await...of 消费
(async () => {
for await (const num of asyncRange) {
console.log(num); // 每隔 100ms 输出 1 2 3 4 5
}
})();
async 生成器:
// async function* 声明异步生成器,yield 返回的值自动包装为 Promise
async function* fetchPages(baseUrl) {
let page = 1;
while (true) {
const res = await fetch(`${baseUrl}?page=${page}`);
const data = await res.json();
if (data.length === 0) return; // 没有更多数据
yield data; // 每次产出一页数据
page++;
}
}
// 消费异步生成器
(async () => {
for await (const page of fetchPages('/api/users')) {
console.log(`获取到 ${page.length} 条数据`);
}
})();
模拟 Node.js 可读流:
async function* readableStream(stream) {
for await (const chunk of stream) {
yield chunk;
}
}
// 批量处理数据流
async function processStream(url) {
const response = await fetch(url);
const gen = readableStream(response.body);
for await (const chunk of gen) {
processChunk(chunk);
}
}
6. 生成器实现惰性序列
斐波那契数列:
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
// 取前 10 个斐波那契数
const fib = fibonacci();
const first10 = Array.from({ length: 10 }, () => fib.next().value);
console.log(first10); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// 只取需要的,内存恒定
function* take(iterable, n) {
const it = iterable[Symbol.iterator]();
for (let i = 0; i < n; i++) {
const { value, done } = it.next();
if (done) return;
yield value;
}
}
console.log([...take(fibonacci(), 10)]); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
素数筛(埃拉托斯特尼筛法的惰性版本):
function* primes() {
const sieve = new Map(); // 合数 → 最小质因子
let num = 2;
while (true) {
if (!sieve.has(num)) {
// num 是质数
yield num;
sieve.set(num * num, num); // 标记其平方
} else {
// num 是合数,更新筛
const prime = sieve.get(num);
const next = num + prime;
// 将下一个合数加入筛(如果还未被标记)
while (sieve.has(next)) next += prime;
sieve.set(next, prime);
sieve.delete(num); // 清理已处理的合数
}
num++;
}
}
// 取前 20 个素数
console.log([...take(primes(), 20)]);
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
通用惰性工具函数:
// 惰性 map
function* lazyMap(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
// 惰性 filter
function* lazyFilter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) yield item;
}
}
// 惰性 take
function* take(iterable, n) {
const it = iterable[Symbol.iterator]();
for (let i = 0; i < n; i++) {
const { value, done } = it.next();
if (done) return;
yield value;
}
}
// 组合使用:从自然数中取前 5 个偶数的平方
function* natural() {
let n = 1;
while (true) yield n++;
}
const result = [
...take(
lazyMap(
lazyFilter(natural(), n => n % 2 === 0),
n => n * n
),
5
),
];
console.log(result); // [4, 16, 36, 64, 100]
7. 生成器与管道/组合
数据处理管道:
// 管道工具:将多个生成器函数串联
function* map(iterable, fn) {
for (const item of iterable) yield fn(item);
}
function* filter(iterable, pred) {
for (const item of iterable) if (pred(item)) yield item;
}
function* flatMap(iterable, fn) {
for (const item of iterable) yield* fn(item);
}
function* take(iterable, n) {
const it = iterable[Symbol.iterator]();
for (let i = 0; i < n; i++) {
const { value, done } = it.next();
if (done) return;
yield value;
}
}
// 实际场景:从原始数据中提取、清洗、转换
const rawUsers = [
{ name: 'Alice', age: 25, active: true },
{ name: 'Bob', age: 17, active: true },
{ name: 'Charlie', age: 30, active: false },
{ name: 'Diana', age: 22, active: true },
{ name: 'Eve', age: 16, active: true },
];
const pipeline = take(
map(
filter(rawUsers, u => u.active && u.age >= 18),
u => ({ displayName: u.name.toUpperCase(), age: u.age })
),
10
);
console.log([...pipeline]);
// [{ displayName: 'ALICE', age: 25 }, { displayName: 'DIANA', age: 22 }]
可组合的中间件模式:
function* middlewarePipeline(context, ...middlewares) {
let index = 0;
function* next() {
if (index < middlewares.length) {
const middleware = middlewares[index++];
yield* middleware(context, next);
}
}
yield* next();
}
// 定义中间件
function* authMiddleware(ctx, next) {
yield { step: 'auth', checking: ctx.token };
if (!ctx.token) { ctx.error = 'Unauthorized'; return; }
yield* next();
}
function* logMiddleware(ctx, next) {
yield { step: 'log', message: 'Request received' };
yield* next();
yield { step: 'log', message: 'Request completed' };
}
function* handlerMiddleware(ctx, next) {
yield { step: 'handler', data: 'Hello World' };
ctx.response = { status: 200, body: 'OK' };
}
const ctx = { token: 'valid-token' };
for (const step of middlewarePipeline(ctx, authMiddleware, logMiddleware, handlerMiddleware)) {
console.log(step);
}
// { step: 'auth', checking: 'valid-token' }
// { step: 'log', message: 'Request received' }
// { step: 'handler', data: 'Hello World' }
// { step: 'log', message: 'Request completed' }
8. 内置可迭代对象
// String — 逐字符迭代
for (const ch of 'Hello') console.log(ch); // H e l l o
[...'Hello']; // ['H', 'e', 'l', 'l', 'o']
// Array
for (const item of [1, 2, 3]) console.log(item);
// Map — 迭代 [key, value] 对
const m = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of m) console.log(key, value);
// Set
for (const value of new Set([1, 2, 2, 3])) console.log(value); // 1 2 3
// NodeList(DOM)
for (const el of document.querySelectorAll('div')) {
el.classList.add('highlight');
}
// arguments
function showArgs() {
for (const arg of arguments) console.log(arg);
}
// TypedArray
for (const byte of new Uint8Array([1, 2, 3])) console.log(byte);
9. 自定义可迭代对象
为类实现 Symbol.iterator:
class Matrix {
constructor(rows, cols) {
this.data = [];
for (let i = 0; i < rows; i++) {
this.data[i] = new Array(cols).fill(0);
}
}
set(r, c, val) {
this.data[r][c] = val;
}
// 按行迭代
*[Symbol.iterator]() {
for (const row of this.data) {
for (const val of row) {
yield val;
}
}
}
// 按列迭代(命名方法,返回生成器)
*byColumn() {
for (let c = 0; c < this.data[0].length; c++) {
for (let r = 0; r < this.data.length; r++) {
yield this.data[r][c];
}
}
}
}
const mat = new Matrix(2, 3);
mat.set(0, 0, 1); mat.set(0, 1, 2); mat.set(0, 2, 3);
mat.set(1, 0, 4); mat.set(1, 1, 5); mat.set(1, 2, 6);
console.log([...mat]); // 按行: [1, 2, 3, 4, 5, 6]
console.log([...mat.byColumn()]); // 按列: [1, 4, 2, 5, 3, 6]
让普通对象可迭代:
const person = {
name: 'Alice',
age: 25,
city: 'Beijing',
// 方法一:生成器方法(最简洁)
*[Symbol.iterator]() {
yield* Object.entries(this);
},
};
for (const [key, value] of person) {
console.log(`${key}: ${value}`);
}
// name: Alice
// age: 25
// city: Beijing
10. 生成器替代状态机
传统状态机 vs 生成器:
// ❌ 传统状态机:状态变量 + switch,逻辑分散
class TrafficLight {
constructor() {
this.state = 'red';
}
next() {
switch (this.state) {
case 'red': this.state = 'green'; return 'green';
case 'green': this.state = 'yellow'; return 'yellow';
case 'yellow': this.state = 'red'; return 'red';
}
}
}
// ✅ 生成器状态机:状态转换一目了然
function* trafficLight() {
while (true) {
yield 'red';
yield 'green';
yield 'yellow';
}
}
const light = trafficLight();
console.log(light.next().value); // 'red'
console.log(light.next().value); // 'green'
console.log(light.next().value); // 'yellow'
console.log(light.next().value); // 'red' — 自动循环
复杂状态机——订单流程:
function* orderFlow() {
const cart = yield 'CART'; // 等待用户加入购物车
const address = yield 'ADDRESS'; // 等待用户填写地址
const payment = yield 'PAYMENT'; // 等待用户支付
return `ORDER_CONFIRMED: ${cart}, ${address}, ${payment}`;
}
const flow = orderFlow();
console.log(flow.next().value); // 'CART'
console.log(flow.next('3 items').value); // 'ADDRESS'
console.log(flow.next('Beijing').value); // 'PAYMENT'
console.log(flow.next('Credit Card').value); // 'ORDER_CONFIRMED: 3 items, Beijing, Credit Card'
常见问题与踩坑
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 生成器不可复用 | 一次 for...of 后迭代器耗尽 | 每次重新调用生成器函数创建新实例 |
| yield 在 finally 中的行为 | return()/throw() 会先执行 finally 块 | finally 中 yield 是合法的,但应避免(语义不清晰) |
| 生成器中的 throw | gen.throw(err) 在当前 yield 处抛出异常 | 生成器内部用 try/catch 包裹 yield 捕获 |
| 与 async/await 的选择 | 生成器做异步是历史方案 | 新代码优先用 async/await;生成器用在惰性序列和迭代场景 |
| for…of 消费后无法重用 | 迭代器是一次性的 | 每次创建新的生成器实例,或在 [Symbol.iterator] 中返回新迭代器 |
| yield 在表达式中的优先级 | yield a + b 等价于 yield (a + b) | 注意优先级,需要时加括号:(yield a) + b |
| 展开运算符消费无限生成器 | [...infiniteGen()] 会死循环 | 用 Array.from({ length: n }, ...) 或 take() 限制数量 |
生成器不可复用示例:
function* gen() { yield 1; yield 2; yield 3; }
const g = gen();
console.log([...g]); // [1, 2, 3] — 已耗尽
console.log([...g]); // [] — 不可复用!
// ✅ 正确做法:每次创建新实例
function getNumbers() { return gen(); } // 工厂函数
console.log([...getNumbers()]); // [1, 2, 3]
console.log([...getNumbers()]); // [1, 2, 3]
yield 在 finally 中的行为:
function* withFinally() {
try {
yield 1;
yield 2;
} finally {
// return() 或 throw() 触发时会先执行 finally
console.log('finally 执行');
yield 'finally-value'; // 合法但不推荐
}
}
const g = withFinally();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return('stop')); // 先执行 finally → { value: 'finally-value', done: false }
console.log(g.next()); // { value: 'stop', done: true }
生成器的 throw 方法:
function* safeGen() {
try {
const input = yield 'prompt';
console.log('收到:', input);
} catch (err) {
console.log('捕获外部抛入:', err.message);
}
yield 'after-catch';
}
const g = safeGen();
g.next(); // 启动,停于 yield 'prompt'
g.throw(new Error('出错了')); // 捕获外部抛入: 出错了
// 返回 { value: 'after-catch', done: false }
最佳实践
- 惰性序列场景优先用生成器(
function*),不要手写迭代器 - 迭代器工厂模式:在
[Symbol.iterator]中返回新迭代器,使对象可多次遍历 - 用
yield*委托组合生成器,避免手动展开嵌套结构 - 生成器做异步已被 async/await 取代,新代码不要用 co 模式
- 异步数据流用
async function*+for await...of - 无限序列配合
take()等工具函数限制数量,防止死循环 - 生成器内部用 try/catch 保护 yield,以便响应外部的
throw()调用 - 状态机用生成器实现比 switch/对象映射更直观
面试题(8题)
Q1: 迭代协议的两个部分分别是什么?各自的要求是什么?
可迭代协议(Iterable Protocol)和迭代器协议(Iterator Protocol)。可迭代协议要求对象实现
[Symbol.iterator]()方法,该方法返回一个迭代器对象。迭代器协议要求对象实现next()方法,该方法返回{ value: any, done: boolean },done为true表示迭代结束。生成器对象同时满足两个协议:它自身有next()方法(迭代器协议),也实现了[Symbol.iterator]()返回自身(可迭代协议),因此生成器既可直接调用next(),也可用于for...of。
Q2: 生成器的 next() 方法传入参数有什么用途?
next(arg)的参数arg会作为上一个yield表达式的返回值。第一次next()传入的参数会被忽略(因为此时还没有yield暂停点)。典型用途是实现双向通信:生成器yield产出值给调用者,调用者通过next(arg)将值传回生成器。例如const input = yield prompt中,yield prompt产出prompt给调用者,下次next(userInput)时input接收userInput。
Q3: yield* 的作用是什么?它的返回值是什么?
yield*用于委托生成器,将迭代控制权交给另一个可迭代对象(生成器、数组、字符串等),逐个产出其所有值,完成后控制权回到当前生成器。yield*表达式的返回值是被委托迭代器的return值(即done: true时的value)。对于普通数组等没有显式return的可迭代对象,yield*的返回值为undefined。典型应用:递归树遍历yield* dfs(child)、数组展开yield* arr。
Q4: 如何让一个普通对象变为可迭代对象?
实现两种方式:1) 实现
[Symbol.iterator]()方法,返回一个符合迭代器协议的对象(有next()方法返回{ value, done })。2) 更简洁的方式是使用生成器:*[Symbol.iterator]() { ...yield... }。例如const obj = { a: 1, b: 2, *[Symbol.iterator]() { yield* Object.values(this); } },之后即可for (const v of obj)或[...obj]。注意:Object默认不可迭代,这是与Map/Set/Array的关键区别。
Q5: 异步迭代器的用法是什么?Symbol.asyncIterator 和 for await...of 如何配合?
异步迭代器实现
[Symbol.asyncIterator]()方法,其next()返回Promise<{ value, done }>。消费时用for await...of循环,它在每次迭代中await next()的 Promise。async function*声明的异步生成器自动实现此协议,yield的值会被包装为 Promise。注意:for await...of必须在async函数内使用;Symbol.asyncIterator和Symbol.iterator是独立的,实现了一个不代表实现了另一个。
Q6: 生成器如何实现懒加载?请举例说明。
生成器通过
yield暂停执行,只在调用next()时才计算并返回下一个值,不会预先计算全部结果,这就是懒加载。例如function* fibonacci() { let a = 0, b = 1; while (true) { yield b; [a, b] = [b, a + b]; } }可以生成无限斐波那契数列但只占用常量内存,因为每次只计算当前值。配合take(gen, n)等工具函数按需取值,即使序列是无限的也不会死循环或撑爆内存。
Q7: 生成器与 async/await 有什么关系?
async/await 本质上是生成器 + 自动执行器(如 co 库)的语法糖。
async function对应function*,await对应yield,JavaScript 引擎内置了自动执行器来驱动 Promise 的 resolve/reject 推进生成器。生成器做异步流程控制的思路:yield一个 Promise,执行器等其 resolve 后将结果通过next(result)传回,遇到reject则通过gen.throw(err)抛回。虽然原理相同,但新代码应优先使用 async/await,因为它是原生语法、调试体验更好;生成器更适合惰性序列和自定义迭代场景。
Q8: 生成器的 throw() 和 return() 方法分别有什么作用?
throw(err)在当前yield暂停点抛出异常,相当于在生成器内部yield处执行了throw err。如果生成器内部有try/catch包裹该yield,异常会被捕获并可以继续执行;否则异常会传播到调用者。return(value)在当前yield暂停点强制终止生成器,返回{ value, done: true }。如果生成器内部有try/finally,return()会先执行finally块。for...of循环在break或提前退出时会自动调用return()来清理生成器资源。
相关链接:
- [[ES6+核心特性]]
- Promise与异步