一、面试开场怎么答更有层次

如果面试官问:

你怎么理解 ES6 中的 Generator?有什么使用场景?

不要直接说"Generator 是一种特殊函数"。
更好的开场方式是从 它的本质能力 说起:

Generator 是 ES6 引入的一种特殊函数,它最核心的能力是:可以暂停执行和恢复执行
普通函数一旦开始执行就会一直运行到结束,而 Generator 可以在执行过程中通过 yield 暂停,把控制权交回给调用方,等调用方再次调用时继续执行。
这种"可中断、可恢复"的特性,让它在处理异步流程、惰性求值、状态机等场景下非常有用。

这个开场就很有深度。


二、什么是 Generator?


1)基本语法

Generator 函数通过 function* 定义,内部使用 yield 暂停:

function* gen() {
  console.log('开始')
  yield 1
  console.log('继续')
  yield 2
  console.log('结束')
  return 3
}

调用方式

const g = gen()

console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: true }

关键点

  • 调用 Generator 函数不会立即执行,而是返回一个遍历器对象(Iterator)
  • 每次调用 .next() 才会执行到下一个 yield
  • yield 后面的值会作为 .next() 返回对象的 value
  • done: true 表示函数执行完毕

2)执行流程图解

function* gen() {
  console.log('step 1')
  yield 'a'
  console.log('step 2')
  yield 'b'
  console.log('step 3')
  return 'c'
}

const g = gen()
调用 输出 返回值
g.next() step 1 { value: 'a', done: false }
g.next() step 2 { value: 'b', done: false }
g.next() step 3 { value: 'c', done: true }

面试加分说法

Generator 函数的执行是"惰性"的,只有调用 .next() 才会推进执行,这和普通函数"一次性执行完"完全不同。


三、Generator 的核心特性

这是面试里最容易拉开差距的部分。


1)可暂停、可恢复

这是 Generator 最本质的特性。

function* gen() {
  yield 1
  yield 2
  yield 3
}

const g = gen()
g.next() // 执行到第一个 yield
// 可以做其他事情
g.next() // 继续执行到第二个 yield

意义

  • 可以把一个长任务拆成多个步骤
  • 每一步可以由外部控制何时执行
  • 适合处理需要"分段执行"的逻辑

2)yield 可以传值,.next() 也可以传值

这是很多人容易忽略的高级用法。

yield 向外传值

function* gen() {
  yield 1
  yield 2
}

const g = gen()
console.log(g.next().value) // 1
console.log(g.next().value) // 2

.next() 向内传值

function* gen() {
  const a = yield 1
  console.log('a =', a)
  const b = yield 2
  console.log('b =', b)
}

const g = gen()
g.next()       // 执行到第一个 yield
g.next(10)     // 把 10 传给 a,输出 a = 10
g.next(20)     // 把 20 传给 b,输出 b = 20

理解

.next(value) 传入的值,会作为上一个 yield 表达式的返回值
这让 Generator 内外可以双向通信。

面试加分点

Generator 不只是单向输出数据,它还可以通过 .next(value) 接收外部传入的值,形成双向数据流。


3)Generator 返回的是 Iterator

Generator 函数返回的对象,符合 Iterator 协议:

function* gen() {
  yield 1
  yield 2
  yield 3
}

const g = gen()

// 可以用 for...of 遍历
for (const value of g) {
  console.log(value) // 1, 2, 3
}

意义

  • Generator 天然可迭代
  • 可以配合 for...of、扩展运算符 ...、解构等使用
const arr = [...gen()] // [1, 2, 3]

4)yield* 委托给另一个 Generator

function* gen1() {
  yield 1
  yield 2
}

function* gen2() {
  yield 'a'
  yield* gen1()
  yield 'b'
}

const g = gen2()
console.log([...g]) // ['a', 1, 2, 'b']

理解

yield* 相当于把另一个 Generator 的所有 yield 展开到当前位置。


四、Generator 的使用场景

这是面试里很想听到的内容,要结合实际说。


1)异步流程控制(历史意义)

这是 Generator 最早被广泛使用的场景。

传统回调地狱

ajax(url1, function(res1) {
  ajax(url2, function(res2) {
    ajax(url3, function(res3) {
      // ...
    })
  })
})

用 Generator + co 库

function* fetchData() {
  const res1 = yield ajax(url1)
  const res2 = yield ajax(url2)
  const res3 = yield ajax(url3)
  console.log(res1, res2, res3)
}

co(fetchData)

理解

Generator 可以让异步代码写得像同步一样,每个 yield 等待异步结果返回后再继续。
这是 async/await 出现之前,社区用 Generator 实现异步流程控制的经典方案。

面试加分说法

Generator 在异步流程控制上的价值,主要是历史意义。
它启发了 async/await 的设计,async/await 本质上就是 Generator + Promise 的语法糖。
现在我们更多直接用 async/await,但理解 Generator 有助于理解 async/await 的底层原理。

这句话非常加分。


2)惰性求值 / 无限序列

Generator 可以生成"按需计算"的序列,不需要一次性生成所有值。

无限序列

function* fibonacci() {
  let a = 0, b = 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

优点

  • 不会一次性占用大量内存
  • 按需计算,性能更好
  • 可以表达无限序列

面试加分点

Generator 的惰性特性,让它非常适合处理大数据集或无限序列,因为它不需要一次性把所有数据加载到内存。


3)状态机

Generator 天然适合实现状态机。

function* stateMachine() {
  while (true) {
    console.log('状态 A')
    yield
    console.log('状态 B')
    yield
    console.log('状态 C')
    yield
  }
}

const sm = stateMachine()
sm.next() // 状态 A
sm.next() // 状态 B
sm.next() // 状态 C
sm.next() // 状态 A(循环)

适用场景

  • 游戏状态切换
  • 流程控制
  • 动画帧控制

4)遍历复杂数据结构

比如遍历树结构:

function* traverseTree(node) {
  yield node.value
  if (node.children) {
    for (const child of node.children) {
      yield* traverseTree(child)
    }
  }
}

const tree = {
  value: 1,
  children: [
    { value: 2 },
    { value: 3, children: [{ value: 4 }] }
  ]
}

console.log([...traverseTree(tree)]) // [1, 2, 3, 4]

5)实现自定义迭代器

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

console.log([...obj]) // [1, 2, 3]

6)控制并发数量

比如控制同时只能有 N 个请求:

function* limitConcurrency(tasks, limit) {
  const executing = []
  for (const task of tasks) {
    const p = task()
    executing.push(p)
    
    if (executing.length >= limit) {
      yield Promise.race(executing)
      executing.splice(executing.findIndex(e => e === p), 1)
    }
  }
}

五、Generator 和 async/await 的关系

这是面试高频追问。


1)async/await 是 Generator 的语法糖

// Generator 写法
function* fetchData() {
  const res1 = yield fetch(url1)
  const res2 = yield fetch(url2)
  return res2
}

// async/await 写法
async function fetchData() {
  const res1 = await fetch(url1)
  const res2 = await fetch(url2)
  return res2
}

对比

对比 Generator async/await
语法 function* + yield async + await
执行 需要手动调用 .next() 自动执行
返回值 Iterator Promise
语义 不够直观 更清晰

2)async/await 的底层实现

async/await 本质上是:

Generator + 自动执行器 + Promise

可以简单理解为:

function asyncToGenerator(generatorFunc) {
  return function() {
    const gen = generatorFunc.apply(this, arguments)
    
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let result
        try {
          result = gen[key](arg)
        } catch (error) {
          return reject(error)
        }
        
        const { value, done } = result
        
        if (done) {
          return resolve(value)
        } else {
          return Promise.resolve(value).then(
            val => step('next', val),
            err => step('throw', err)
          )
        }
      }
      
      step('next')
    })
  }
}

面试加分说法

async/await 可以理解为"自动执行的 Generator + Promise 包装"。
Generator 需要手动调用 .next() 推进执行,而 async 函数会自动执行,并且把结果包装成 Promise。
所以理解 Generator,对理解 async/await 的底层原理很有帮助。

这句话非常有深度。


六、Generator 的优缺点

主动说优缺点会显得成熟。


优点

1)可暂停、可恢复

适合分段执行的场景。

2)惰性求值

按需计算,节省内存。

3)双向通信

内外可以互相传值。

4)天然支持迭代

返回 Iterator,可配合 for...of 等使用。


缺点

1)语法不够直观

不如 async/await 清晰。

2)需要手动执行

不像 async 函数自动执行。

3)错误处理相对复杂

需要通过 .throw() 或 try...catch 配合。

4)现在更多是底层能力

实际开发中直接用 Generator 的场景越来越少,更多是理解异步原理时用到。


七、面试标准回答

我理解 Generator 是 ES6 引入的一种特殊函数,它最核心的能力是可以暂停执行和恢复执行
普通函数一旦开始就会一直运行到结束,而 Generator 可以通过 yield 暂停,把控制权交回给调用方,等调用方再次调用 .next() 时继续执行。

语法上,Generator 用 function* 定义,内部用 yield 暂停。调用 Generator 函数不会立即执行,而是返回一个遍历器对象(Iterator),每次调用 .next() 才会执行到下一个 yield
它有几个核心特性:
第一,可暂停、可恢复,适合分段执行的场景;
第二,yield 可以向外传值,.next() 也可以向内传值,形成双向通信;
第三,返回的是 Iterator,天然支持 for...of 等迭代操作;
第四,yield* 可以委托给另一个 Generator。

使用场景方面,我觉得主要有几个:
第一是异步流程控制,这是 Generator 最早被广泛使用的场景,通过 Generator + co 库可以让异步代码写得像同步一样,这也是 async/await 的前身;
第二是惰性求值和无限序列,比如斐波那契数列,Generator 可以按需计算,不需要一次性生成所有值;
第三是状态机,Generator 的暂停恢复特性天然适合实现状态切换;
第四是遍历复杂数据结构,比如树的遍历;
第五是实现自定义迭代器。

和 async/await 的关系上,async/await 本质上是 Generator + 自动执行器 + Promise 的语法糖。Generator 需要手动调用 .next() 推进执行,而 async 函数会自动执行并返回 Promise。所以理解 Generator,对理解 async/await 的底层原理很有帮助。

总体上,我觉得 Generator 在现代开发中更多是一种底层能力,实际业务代码里直接用的场景不多,更多是用 async/await。但它在理解异步原理、实现惰性求值、状态机等场景下依然很有价值。


八、精简版面试回答

Generator 是 ES6 的一种特殊函数,核心能力是可以暂停执行和恢复执行。
用 function* 定义,内部用 yield 暂停,调用后返回 Iterator,每次 .next() 执行到下一个 yield
它的特点是可暂停恢复、支持双向通信、天然可迭代、支持惰性求值。
主要使用场景有异步流程控制(async/await 的前身)、惰性求值(无限序列)、状态机、遍历复杂结构等。
async/await 本质上是 Generator + 自动执行器 + Promise 的语法糖,所以理解 Generator 有助于理解异步原理。
现在实际开发中更多直接用 async/await,但 Generator 在理解底层原理和特定场景下依然有价值。


九、如果想答得更高级,可以补这几句


1)Generator 的本质是协程

Generator 可以理解为 JavaScript 中的"协程"(Coroutine)实现。
协程是一种比线程更轻量的并发单元,可以在执行过程中主动让出控制权,等待被重新唤醒。
Generator 的 yield 就是让出控制权,.next() 就是唤醒。


2)Generator 和 Iterator 的关系

Generator 函数返回的对象,本身就是一个 Iterator。
所以 Generator 是实现自定义迭代器最简单的方式。


3)为什么现在用得少了?

Generator 在异步场景下的价值,主要是历史意义。
async/await 出现后,语法更直观、自动执行、错误处理更方便,所以现在异步场景基本都用 async/await
但 Generator 在惰性求值、状态机、自定义迭代器等场景下依然有独特价值。


十、一句话总结

面试官真正想听的是:

  • 你是否知道 Generator 的语法和执行机制
  • 你能不能说清楚 可暂停恢复、双向通信 这些核心特性
  • 你是否理解 Generator 和 async/await 的关系
  • 你能不能结合 异步控制、惰性求值、状态机 等实际场景来讲
  • 你有没有意识到 Generator 更多是底层能力,现在直接用得少了
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐