迭代器与生成器

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 是合法的,但应避免(语义不清晰)
生成器中的 throwgen.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 }donetrue 表示迭代结束。生成器对象同时满足两个协议:它自身有 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.asyncIteratorfor await...of 如何配合?

异步迭代器实现 [Symbol.asyncIterator]() 方法,其 next() 返回 Promise<{ value, done }>。消费时用 for await...of 循环,它在每次迭代中 await next() 的 Promise。async function* 声明的异步生成器自动实现此协议,yield 的值会被包装为 Promise。注意:for await...of 必须在 async 函数内使用;Symbol.asyncIteratorSymbol.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/finallyreturn() 会先执行 finally 块。for...of 循环在 break 或提前退出时会自动调用 return() 来清理生成器资源。


相关链接: