javascript

迭代器与生成器

By AI-Writer 12 min read

迭代器(Iterator)和生成器(Generator)是 JavaScript 中处理序列数据的强大抽象。它们让惰性计算无限序列异步数据流变得优雅可控。

可迭代协议(Iterable Protocol)

一个对象要成为可迭代对象,必须实现 @@iterator 方法(即 Symbol.iterator):

javascript
const iterable = {
  data: [1, 2, 3],
  [Symbol.iterator]() {
    let index = 0;
    const data = this.data;

    return {
      next() {
        if (index < data.length) {
          return { value: data[index++], done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

// 使用 for...of 消费
for (const value of iterable) {
  console.log(value); // 1, 2, 3
}

// 使用展开运算符
console.log([...iterable]); // [1, 2, 3]

原生可迭代对象

JavaScript 内置了多个可迭代对象:

对象迭代内容
Array数组元素
String字符(Unicode 码点)
Map[key, value] 键值对
Set集合元素
TypedArray类型化数组元素
arguments函数参数
NodeListDOM 节点列表

Object 默认不可迭代。for...in 遍历的是键,不是迭代器协议。

迭代器协议(Iterator Protocol)

迭代器是一个对象,必须实现 next() 方法,返回 { value, done } 格式:

javascript
// 手动创建迭代器
function createRangeIterator(start, end) {
  let current = start;

  return {
    next() {
      if (current <= end) {
        return { value: current++, done: false };
      }
      return { value: undefined, done: true };
    },
    // 可选:让迭代器本身也可迭代
    [Symbol.iterator]() {
      return this;
    }
  };
}

const range = createRangeIterator(1, 3);
console.log(range.next()); // { value: 1, done: false }
console.log(range.next()); // { value: 2, done: false }
console.log(range.next()); // { value: 3, done: false }
console.log(range.next()); // { value: undefined, done: true }

生成器函数(Generator)

生成器函数使用 function* 声明,内部使用 yield 暂停执行:

javascript
function* countUpTo(max) {
  let count = 1;
  while (count <= max) {
    yield count++; // 暂停,返回当前值
  }
}

const counter = countUpTo(3);
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { value: 3, done: false }
console.log(counter.next()); // { value: undefined, done: true }

// 生成器对象本身是可迭代的
for (const num of countUpTo(5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

yield 的多种用法

javascript
function* demo() {
  // 1. 基本 yield:返回值
  yield 1;

  // 2. yield*:委托给另一个可迭代对象
  yield* [2, 3];

  // 3. yield 表达式:接收外部传入的值
  const received = yield 'waiting for input';
  console.log('received:', received); // 从 next(value) 传入

  return 'done';
}

const gen = demo();
console.log(gen.next());      // { value: 1, done: false }
console.log(gen.next());      // { value: 2, done: false }
console.log(gen.next());      // { value: 3, done: false }
console.log(gen.next());      // { value: 'waiting for input', done: false }
console.log(gen.next('hello')); // received: hello, 然后 { value: 'done', done: true }

yield* 委托迭代

yield* 可以将迭代委托给另一个生成器或任何可迭代对象:

javascript
function* genA() {
  yield 'A1';
  yield 'A2';
}

function* genB() {
  yield 'B1';
  yield* genA();      // 委托给 genA
  yield* [1, 2, 3];   // 委托给数组
  yield 'B2';
}

console.log([...genB()]);
// ['B1', 'A1', 'A2', 1, 2, 3, 'B2']

惰性计算与无限序列

生成器的最大优势是惰性求值——只在需要时计算下一个值:

javascript
// 无限斐波那契序列
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3

// 取前 10 个(不会计算全部)
const first10 = [...take(fibonacci(), 10)];
console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

function* take(iterable, n) {
  let count = 0;
  for (const item of iterable) {
    if (count >= n) return;
    yield item;
    count++;
  }
}

生成器的状态控制

return() 与 throw()

javascript
function* withCleanup() {
  try {
    yield 'step 1';
    yield 'step 2';
    yield 'step 3';
  } finally {
    console.log('cleanup!'); // 无论怎么结束都会执行
  }
}

const gen = withCleanup();
console.log(gen.next()); // { value: 'step 1', done: false }

// 提前终止
console.log(gen.return('early exit')); // { value: 'early exit', done: true }
// 输出: cleanup!

// throw 向生成器内抛出错误
const gen2 = withCleanup();
gen2.next();
try {
  gen2.throw(new Error('something wrong'));
} catch (e) {
  console.log(e.message); // something wrong
}
// 输出: cleanup!

提前终止与 for…of

for...of 遇到 breakreturnthrow 时,会自动调用迭代器的 return() 方法:

javascript
function* resourceGenerator() {
  try {
    console.log('acquiring resource');
    yield 'resource';
  } finally {
    console.log('releasing resource'); // 即使 break 也会执行
  }
}

for (const res of resourceGenerator()) {
  console.log(res);
  break; // 触发 return()
}
// 输出: acquiring resource → resource → releasing resource

异步生成器

ES2018 引入 async function*for await...of,处理异步数据流:

javascript
async function* fetchPages(baseUrl) {
  let page = 1;
  while (true) {
    const response = await fetch(`${baseUrl}?page=${page}`);
    const data = await response.json();

    if (data.items.length === 0) break;

    for (const item of data.items) {
      yield item;
    }

    page++;
  }
}

// 消费异步生成器
async function main() {
  for await (const item of fetchPages('/api/items')) {
    console.log(item.name);
  }
}

异步迭代器手动实现

javascript
const asyncIterable = {
  data: [1, 2, 3],
  [Symbol.asyncIterator]() {
    let index = 0;
    const data = this.data;

    return {
      async next() {
        if (index < data.length) {
          // 模拟异步操作
          await new Promise(r => setTimeout(r, 100));
          return { value: data[index++], done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

(async () => {
  for await (const value of asyncIterable) {
    console.log(value); // 1, 2, 3(间隔 100ms)
  }
})();

实战应用

自定义树结构遍历

javascript
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'),
]);

console.log([...tree]); // ['root', 'A', 'A1', 'A2', 'B']

组合多个数据源

javascript
function* mergeSorted(a, b) {
  const iterA = a[Symbol.iterator]();
  const iterB = b[Symbol.iterator]();

  let nextA = iterA.next();
  let nextB = iterB.next();

  while (!nextA.done || !nextB.done) {
    if (nextB.done || (!nextA.done && nextA.value <= nextB.value)) {
      yield nextA.value;
      nextA = iterA.next();
    } else {
      yield nextB.value;
      nextB = iterB.next();
    }
  }
}

const merged = [...mergeSorted([1, 3, 5], [2, 4, 6])];
console.log(merged); // [1, 2, 3, 4, 5, 6]

小结

特性说明
Symbol.iterator使对象可迭代
next()返回 { value, done }
function*生成器函数声明
yield暂停并返回值
yield*委托给另一个可迭代对象
async function*异步生成器
for await...of消费异步可迭代对象

迭代器和生成器将控制流(何时暂停、何时继续)从数据生产中解耦。它们不仅是语法糖,更是处理无限序列、异步流和复杂遍历逻辑的底层基础设施。

#javascript #iterator #generator #异步生成器 #协议

评论

A

Written by

AI-Writer

Related Articles

javascript
#17

排序与搜索算法

手写归并排序、快速排序、堆排序,掌握二分搜索与分治策略,理解时间复杂度与大 O 表示法。

Read More
javascript
#18

迭代器与生成器

深入可迭代协议与迭代器协议,掌握生成器函数(function*)、yield 表达式与异步生成器,手写自定义可迭代对象。

Read More