为什么这段嵌套ES6 Generator的代码无法按预期输出递增序列?
问题根源分析
- 生成器遍历的中断传播机制是核心矛盾:你在b生成器中用
for...of直接消费gen实例时,只要循环因为达到阈值执行return提前终止,引擎就会自动调用gen的return()方法,把gen标记为已完成状态,彻底打断gen的连续执行流。这和你预期的“停下后下次继续”的特性完全冲突。你观察到反复输出0-10的现象,就是上述机制和a生成器的冗余逻辑叠加的结果:当gen被强制中断关闭时,a生成器的执行流会重置到初始位置,递归新建的a()实例会重新从0开始生成序列,最终表现就是每次都从0开始输出固定区间的值。
- a生成器的实现逻辑冗余:你在a中写完0-99的循环后,又嵌套了一段新建a()实例的遍历逻辑,这不仅没有实际作用,还会在生成器被中断时触发序列重置,放大了传播机制带来的问题。
- 计数逻辑存在偏差:b函数中
if (i++ > subsetSize) return的判断,每次会多产出一个值才触发终止,和预期的“指定大小子集”的目标不符,也容易打乱序列的位置计算。
修正实现
核心思路是避开for...of对gen的遍历操作,手动控制gen的next()调用,从根源上避免触发引擎自动关闭gen的传播逻辑,完整保留gen的执行进度。
- 先简化a生成器,去掉冗余的递归逻辑,保证序列生成的连续性:
const a = function* (): Generator<number> { for (let i = 0; i < 100; i++) { yield i; } // 如果需要更长的无限序列,直接在这里补充后续yield逻辑即可,不需要嵌套新建生成器 } - 重写b生成器,手动拉取指定数量的值,拿满就暂停返回,不触发gen的关闭流程:
const b = function* (gen: Generator<number>, subsetSize: number): Generator<number> { for (let i = 0; i < subsetSize; i++) { const { value, done } = gen.next(); // gen遍历完成后才打印Done if (done) { console.log("Done"); return; } yield value; } }
运行效果
修正后每次调用b(gen, 10)时:
- 会从gen上次暂停的位置开始,连续取出10个值yield出去
- 拿满10个值后b的生成器自动结束,全程不会调用gen的return()方法,gen的执行进度会被完整保留
- 下次调用b时,会直接从上次停止的位置继续取数,直到gen把所有值产出完毕,才会打印
Done - 最终输出顺序会是0-9、10-19、20-29……直到90-99,最后打印Done,完全符合预期。
内容的提问来源于stack exchange,提问作者J-Cake
相关产品推荐
相关产品推荐

