JavaScript 迭代器、生成器、yield、惰性计算、无限序列与异步生成器,是进阶异步与数据流编程的核心基础。


一、开篇:能暂停的函数

如果一个函数可以执行到一半暂停、返回一个值,之后再从暂停处恢复执行,会怎样?如果能创建一个按需计算的序列(而非一次性全部生成),又会怎样?

// 可以暂停、恢复的函数
function* countToThree() {
  yield 1  // 暂停,返回 1
  yield 2  // 恢复,暂停,返回 2
  yield 3  // 恢复,暂停,返回 3
}

const counter = countToThree()
console.log(counter.next().value) // 1
console.log(counter.next().value) // 2
console.log(counter.next().value) // 3

这就是生成器(Generator)。ES2015 引入,配合迭代器,实现:

  • 惰性求值
  • 无限序列
  • 清晰的数据管道
  • 可控的暂停 / 恢复执行

二、本文你将学到

  • 迭代器与迭代协议
  • 生成器函数 function*yield
  • yieldreturn 的区别
  • Symbol.iterator 让任何对象可迭代
  • 惰性求值与内存效率
  • 分页、ID 生成、状态机等实战模式
  • 异步生成器与 for await...of

前置知识:闭包、高阶函数


三、什么是迭代器(Iterator)?

迭代器是一个对象,表示一个序列,提供逐个取值的方法。必须具备 .next() 方法,返回结构:

js

{ value: 下一个值, done: 是否结束 }

示例:手动实现迭代器

function createCounterIterator(max) {
  let count = 0
  return {
    next() {
      if (count < max) {
        return { value: count++, done: false }
      } else {
        return { value: undefined, done: true }
      }
    }
  }
}

为什么用迭代器?

  1. 惰性求值:用到才计算,不预先全量生成
  2. 极高内存效率:不用把整个序列放入内存

四、可迭代对象(Iterable)

实现了 Symbol.iterator 方法的对象,就是可迭代的。

JS 内置可迭代对象

  • Array
  • String
  • Map
  • Set
  • arguments
  • NodeList

for...of、展开运算符、解构 底层都用迭代器。


五、售货机类比(生成器最易懂的比喻)

你 = 调用者售货机 = 生成器

  • 按按钮 → 调用 .next()
  • 掉出一个商品 → yield 返回值
  • 机器暂停等待 → 生成器暂停
  • 记住位置 → 生成器保存状态
  • 卖完 → done: true

六、什么是生成器(Generator)?

生成器是可以暂停和恢复执行的函数。

  • 声明:function*(带星号)
  • 暂停:yield
  • 恢复:.next()

关键特性

调用生成器函数不会立刻执行,只返回一个生成器对象(迭代器)。只有调用 .next() 才会执行到下一个 yield


七、yield 深度解析

基础用法

function* demo() {
  console.log('开始')
  yield '第一个值'
  console.log('恢复')
  yield '第二个值'
  console.log('结束')
  return '完成'
}

yield vs return

特性 yield return
作用 暂停生成器 终止生成器
done 状态 false true
数量 可以多个 只有最后一个有效
for...of 可见 可见 不可见

注意return 的值不会被 for...of/ 展开运算符捕获。


八、yield*:委托迭代

把另一个可迭代对象的结果全部 yield 出来。常用于递归展开、嵌套结构扁平化

function* inner() { yield 'a'; yield 'b' }
function* outer() { yield 1; yield* inner(); yield 2 }

console.log([...outer()]) // [1,'a','b',2]

九、向生成器内部传值

可以通过 .next(值) 把数据传入生成器,成为上一个 yield 的返回值。

function* chat() {
  const name = yield '你叫什么名字?'
  yield `你好,${name}!`
}

const gen = chat()
gen.next().value       // "你叫什么名字?"
gen.next('Alice').value // "你好,Alice!"

十、生成器控制方法

1. .return(值)

立刻终止生成器

gen.return('终止') // { value:'终止', done:true }

2. .throw(错误)

在当前 yield 位置抛出错误

gen.throw(new Error('出错啦'))

十一、迭代协议(核心)

让自定义对象支持 for...of、展开运算符,只需实现:

[Symbol.iterator]() {
  return 迭代器
}

极简写法(用生成器)

const obj = {
  items: [1,2,3],
  *[Symbol.iterator]() {
    yield* this.items
  }
}

for(let x of obj) console.log(x)

十二、惰性求值 & 无限序列

生成器最强大的能力:用到才计算

无限序列(数组不可能做到)

function* naturalNumbers() {
  let n = 1
  while(true) yield n++
}

只取前 N 个

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

十三、五大实战模式

1. 唯一 ID 生成器

function* createId(prefix) {
  let id = 1
  while(true) yield `${prefix}_${id++}`
}

2. 分页 / 分块处理

function* chunk(arr, size) {
  for(let i=0; i<arr.length; i+=size) {
    yield arr.slice(i, i+size)
  }
}

3. 数据管道:filter + map

function* filter(iter, fn) { for(const v of iter) if(fn(v)) yield v }
function* map(iter, fn) { for(const v of iter) yield fn(v) }

4. 状态机(红绿灯)

function* light() {
  while(true) {
    yield 'green'
    yield 'yellow'
    yield 'red'
  }
}

5. 树结构遍历(递归生成器)

function* traverse(node) {
  yield node.value
  if(node.children) for(const c of node.children) yield* traverse(c)
}

十四、异步生成器(Async Generator)

同步生成器只能同步值,异步生成器可以 await 并逐一流出异步数据。

语法

async function* fetchPages() {
  let page = 1
  while(true) {
    const res = await fetch(`/api?page=${page++}`)
    const data = await res.json()
    if(!data.items) return
    yield data.items
  }
}

消费

for await (const items of fetchPages()) {
  console.log(items)
}

适用场景:

  • 分页接口流式读取
  • 文件逐行读取
  • 流式处理大数据
  • 数据库游标

十五、常见错误(高频坑)

  1. 忘记写 function* 的星号 → SyntaxError
  2. 调用生成器不执行 → 必须 .next() 或迭代
  3. 用 return 给迭代传值 → for...of 拿不到
  4. 耗尽的生成器重复使用 → 返回空
  5. 无限生成器直接展开 → 卡死 [...forever()]
  6. 小数据量用生成器 → 数组更简单

十六、核心总结

  • 迭代器:.next(){value,done}
  • 生成器:function* + yield → 可暂停函数
  • yield 暂停,return 终止
  • yield* 委托迭代
  • 惰性求值 = 高性能 + 无限序列
  • Symbol.iterator → 让对象可迭代
  • 异步生成器:async function* + for await...of

📦 概念 汇总

1. 核心基础

  • 迭代器 Iterator同类:遍历器、序列访问器、游标
  • 生成器 Generator同类:协程、暂停函数、可中断函数
  • 迭代协议 Iterable Protocol同类:可遍历接口、统一遍历标准
  • Symbol.iterator同类:内置迭代入口、遍历工厂

2. 语法与关键字

  • function*同类:生成器声明、协程函数
  • yield同类:暂停、产出、挂起
  • yield*同类:委托迭代、展开迭代、递归遍历
  • .next()同类:步进、恢复执行
  • .return()同类:终止、关闭、清理
  • .throw()同类:错误注入、异常触发

3. 求值与性能

  • 惰性求值 Lazy Evaluation同类:按需计算、延迟计算、按需加载
  • 急切求值 Eager Evaluation同类:预先计算、全量生成
  • 无限序列 Infinite Sequence同类:无限流、无穷数列、无界迭代

4. 流程模式

  • 数据管道 Data Pipeline同类:链式处理、流操作、filter/map
  • 状态机 State Machine同类:流程控制、状态流转、步骤执行
  • 分页生成器同类:分批加载、懒加载、游标分页
  • 扁平化遍历同类:递归展开、树摊平、深度遍历

5. 异步扩展

  • 异步生成器 Async Generator同类:异步流、异步迭代器
  • for await...of同类:异步遍历、流消费
  • 流式处理 Streaming同类:逐块处理、渐进式加载

6. 上层关联(你学过的)

  • Promise同类:异步结果占位符
  • async/await同类:协程、暂停恢复(基于 Promise)
  • 回调 Callback同类:异步控制(旧式)
  • 事件循环同类:任务调度、异步执行

更多推荐