是否可以将ECMAScript 6发生器重置为其初始状态?

dvl*_*lsg 30 javascript generator ecmascript-harmony ecmascript-6

我的问题是:鉴于提供的(非常简单的)生成器,是否可以将生成器恢复到其原始状态以便再次使用?

var generator = function*() {
    yield 1;
    yield 2;
    yield 3;
};

var iterable = generator();

for (let x of iterable) {
    console.log(x);
}

// At this point, iterable is consumed.
// Is there a method for moving iterable back
// to the start point by only without re-calling generator(),
// (or possibly by re-calling generator(), only by using prototype 
//  or constructor methods available within the iterable object)
// so the following code would work again?

for (let x of iterable) {
    console.log(x);
}
Run Code Online (Sandbox Code Playgroud)

我希望能够将iterable传递给其他一些范围,迭代它,做一些其他的东西,然后能够在稍后的同一范围内再次迭代它.

Azd*_*der 17

如果你的意图是

到某个其他范围,迭代它,做一些其他的东西,然后能够在稍后的同一范围内再次迭代它.

那么你不应该尝试做的唯一事情就是传递迭代器,而不是传递生成器:

var generator = function*() {
    yield 1;
    yield 2;
    yield 3;
};

var user = function(generator){

    for (let x of generator()) {
        console.log(x);
    }

    for (let x of generator()) {
        console.log(x);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者只是制作一个"循环"迭代器并在迭代时进行检查

var generator = function*() {
    while(true){
        yield 1;
        yield 2;
        yield 3;
    }
};

for( x in i ){
    console.log(x);
    if(x === 3){
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ber*_*rgi 13

此时,消耗了iterable.

这意味着它的内部[[GeneratorState]]是completed.

有没有一种方法可以通过重新调用generator()来将iterable移回起始点

没有.规范说明

一旦生成器进入"已完成"状态,它就永远不会离开它,并且永远不会恢复其相关的执行上下文.此时可以丢弃与生成器关联的任何执行状态.

或者可能只通过使用可迭代对象中可用的原型或构造方法重新调用generator()

虽然未在规范中明确说明,但可迭代对象上没有比[[GeneratorState]]和[[GeneratorContext]] 更多的特定于实例的属性.

然而,信息性的"发电机对象关系"图形状态:

每个Generator函数都有一个没有构造函数属性的关联原型.因此,生成器实例不公开对其生成器函数的访问.

我希望能够将iterable传递给其他范围

改为通过发电机功能.或者产生新生成器实例的东西.


jfr*_*d00 5

尽我所能说这是不可能的。根据这个有用的维基和生成器上 ES6草案版本,一旦你从它返回(而不是让步),它就会将它放入"closed"状态,并且无法将它移回"newborn"状态,这就是新生成器的方式开始。

您可能必须将回调传递到其他范围以创建新的生成器。作为一种变通方法,如果需要,您甚至可以将该回调作为自定义方法添加到发送到另一个作用域的生成器上,并且该回调将为另一个作用域创建一个新的生成器。

如果您考虑生成器的工作方式,它们必须从头开始执行以重置其初始状态,而且根本没有理由支持这一点。这类似于询问为什么不能在现有对象上重新执行构造函数并期望在同一对象中有一个原始对象。虽然这一切在技术上都是可行的,但要使工作正常进行是一件很麻烦的事情,而且真的没有理由支持它。如果你想要一个原始对象,只需创建一个新对象。与发电机相同。


这有点骇人听闻,但值得考虑。你可以制作一个重复的发电机。假设你的生成器是这样工作的:

var generator = function*() {
    while (true) {
        yield 1;
        yield 2;
        yield 3;
        yield null;
    }
};

var iterable = generator();

for (let x of iterable) {
    if (x === null) break;
    console.log(x);
}

// generator is now in a state ready to repeat again
Run Code Online (Sandbox Code Playgroud)

我可以很容易地看出这可能是一种反模式,因为如果你这样做:

for (let x of iterable) {
    console.log(x);
}
Run Code Online (Sandbox Code Playgroud)

您将有一个无限循环,因此必须非常小心地使用它。仅供参考,上面的 wiki 显示了无限斐波那契数列的示例,因此当然可以考虑使用无限生成器。


the*_*eye 5

根据ES6草案版本

一旦生成器进入"completed"状态,它就永远不会离开它,并且永远不会恢复其关联的执行上下文。此时可以丢弃与生成器关联的任何执行状态。

因此,一旦完成就无法重置它。这样做也是有道理的。我们称它为生成器,这是有原因的:)


Jon*_*n z 5

每当您需要“重置”一个可迭代对象时,只需将旧的扔掉并制作一个新的。

var generator = function*() {
    yield 1;
    yield 2;
    yield 3;
};
const makeIterable = () => generator()

for (let x of makeIterable()) {
    console.log(x);
}

// At this point, iterable is consumed.
// Is there a method for moving iterable back
// to the start point by only without re-calling generator(),
// (or possibly by re-calling generator(), only by using prototype 
//  or constructor methods available within the iterable object)
// so the following code would work again?

for (let x of makeIterable()) {
    console.log(x);
}
Run Code Online (Sandbox Code Playgroud)