为什么这个生成器函数会中断?
Why does this generator function break?
我正在研究 ES6 生成器,因为它们最近大肆宣传。我的目标是拥有一个生成较大生成器的子集并停止的生成器。然而,当再次调用生成器时,它不会重复序列,而是继续。很像 ES6 生成器。换句话说,我有一个嵌套的生成器。
const a = function* (): Generator<number> {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen: Generator<number>, subsetSize: number): Generator<number> {
let i = 0;
for (const j of gen) {
if (i++ > subsetSize)
return;
else
yield j;
}
console.log("Done");
}
const gen = a();
for (let i = 0; i < 150; i++)
for (const j of b(gen, 10))
console.log(j);
我期望此代码执行的操作是打印数字 0-10,打印 Done
,然后打印 10-20,打印 Done
等等。然而,实际输出是0-10然后Done
重复。我不确定为什么,也不知道如何获得我正在寻找的结果。
这是因为 b
的 for (const j of gen) {
中的 return
,以及循环调用 for (const j of b(gen, 10))
的事实。
当您 return
退出 for-of
循环时,它会调用正在迭代的生成器的 return
method (gen
在这种情况下,来自 a
).这完成了生成器,因此所有后续尝试使用该生成器(因为对 b
的调用重用 gen
并且这些调用处于循环中)只是继续返回相同的值。 (循环中的 break
也会调用生成器上的 return
。)
这是一个更简单的例子:
function* example() {
let i = 0;
while (true) {
yield i++;
}
}
function use(gen) {
let counter = 0;
console.log("Starting for-of on gen");
for (const value of gen) {
console.log(value);
if (value > 5) {
console.log("Returning early");
return;
}
}
console.log("Done with for-of on gen");
}
const gen = example();
use(gen);
use(gen);
use(gen);
.as-console-wrapper {
max-height: 100% !important;
}
如果您不想要 for-of
的自动行为,您可以直接在生成器上调用 next
而不是使用 for-of
。 (如果你想要 0-9
、"Done"
、10-19
、"Done"
等,你还必须调整你的计数器并移动你输出 "Done"
的位置。 ) 例子:
const a = function* ()/*: Generator<number>*/ {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen/*: Generator<number>*/, subsetSize/*: number*/)/*: Generator<number>*/ {
let i = 0;
let result;
while (!(result = gen.next()).done) {
yield result.value;
if (++i >= subsetSize) {
console.log("Done");
return;
}
}
}
const gen = a();
for (let i = 0; i < /*150*/3; i++)
for (const j of b(gen, 10))
console.log(j);
.as-console-wrapper {
max-height: 100% !important;
}
或者,您可以将生成器包装在一个仅转发 next
调用(并实现 [Symbol.iterator]
)的对象中:
const continuingIterator = it => {
return {
[Symbol.iterator]() {
return this;
},
next() {
return it.next();
},
};
};
那么就是:
for (const j of continuingIterator(gen)) {
实例:
const continuingIterator = it => {
return {
[Symbol.iterator]() {
return this;
},
next() {
return it.next();
},
};
};
const a = function* ()/*: Generator<number>*/ {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen/*: Generator<number>*/, subsetSize/*: number*/)/*: Generator<number>*/ {
let i = 0;
for (const j of continuingIterator(gen)) {
yield j;
if (++i >= subsetSize) {
console.log("Done");
return;
}
}
}
const gen = a();
for (let i = 0; i < /*150*/3; i++)
for (const j of b(gen, 10))
console.log(j);
.as-console-wrapper {
max-height: 100% !important;
}
我正在研究 ES6 生成器,因为它们最近大肆宣传。我的目标是拥有一个生成较大生成器的子集并停止的生成器。然而,当再次调用生成器时,它不会重复序列,而是继续。很像 ES6 生成器。换句话说,我有一个嵌套的生成器。
const a = function* (): Generator<number> {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen: Generator<number>, subsetSize: number): Generator<number> {
let i = 0;
for (const j of gen) {
if (i++ > subsetSize)
return;
else
yield j;
}
console.log("Done");
}
const gen = a();
for (let i = 0; i < 150; i++)
for (const j of b(gen, 10))
console.log(j);
我期望此代码执行的操作是打印数字 0-10,打印 Done
,然后打印 10-20,打印 Done
等等。然而,实际输出是0-10然后Done
重复。我不确定为什么,也不知道如何获得我正在寻找的结果。
这是因为 b
的 for (const j of gen) {
中的 return
,以及循环调用 for (const j of b(gen, 10))
的事实。
当您 return
退出 for-of
循环时,它会调用正在迭代的生成器的 return
method (gen
在这种情况下,来自 a
).这完成了生成器,因此所有后续尝试使用该生成器(因为对 b
的调用重用 gen
并且这些调用处于循环中)只是继续返回相同的值。 (循环中的 break
也会调用生成器上的 return
。)
这是一个更简单的例子:
function* example() {
let i = 0;
while (true) {
yield i++;
}
}
function use(gen) {
let counter = 0;
console.log("Starting for-of on gen");
for (const value of gen) {
console.log(value);
if (value > 5) {
console.log("Returning early");
return;
}
}
console.log("Done with for-of on gen");
}
const gen = example();
use(gen);
use(gen);
use(gen);
.as-console-wrapper {
max-height: 100% !important;
}
如果您不想要 for-of
的自动行为,您可以直接在生成器上调用 next
而不是使用 for-of
。 (如果你想要 0-9
、"Done"
、10-19
、"Done"
等,你还必须调整你的计数器并移动你输出 "Done"
的位置。 ) 例子:
const a = function* ()/*: Generator<number>*/ {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen/*: Generator<number>*/, subsetSize/*: number*/)/*: Generator<number>*/ {
let i = 0;
let result;
while (!(result = gen.next()).done) {
yield result.value;
if (++i >= subsetSize) {
console.log("Done");
return;
}
}
}
const gen = a();
for (let i = 0; i < /*150*/3; i++)
for (const j of b(gen, 10))
console.log(j);
.as-console-wrapper {
max-height: 100% !important;
}
或者,您可以将生成器包装在一个仅转发 next
调用(并实现 [Symbol.iterator]
)的对象中:
const continuingIterator = it => {
return {
[Symbol.iterator]() {
return this;
},
next() {
return it.next();
},
};
};
那么就是:
for (const j of continuingIterator(gen)) {
实例:
const continuingIterator = it => {
return {
[Symbol.iterator]() {
return this;
},
next() {
return it.next();
},
};
};
const a = function* ()/*: Generator<number>*/ {
for (let i = 0; i < 100; i++)
yield i;
for (const i of a())
yield i;
}
const b = function* (gen/*: Generator<number>*/, subsetSize/*: number*/)/*: Generator<number>*/ {
let i = 0;
for (const j of continuingIterator(gen)) {
yield j;
if (++i >= subsetSize) {
console.log("Done");
return;
}
}
}
const gen = a();
for (let i = 0; i < /*150*/3; i++)
for (const j of b(gen, 10))
console.log(j);
.as-console-wrapper {
max-height: 100% !important;
}