Возможно ли, чтобы reset генератор 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);
}

Я хотел бы иметь возможность передавать итерируемую часть в какую-то другую область, перебирать ее, делать некоторые другие вещи, а затем снова иметь возможность перебирать ее в той же области.

Ответ 1

Если ваше намерение

в какой-то другой области, перебирайте его, делайте некоторые другие вещи, затем сможете повторять его снова позже в той же области видимости.

Тогда единственное, что вам не следует делать, это передать итератор, вместо этого передать генератор:

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);
    }
}

Или просто создайте итератор "round robin" и проверьте при повторении

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

for( x in i ){
    console.log(x);
    if(x === 3){
        break;
    }
}

Ответ 2

В этот момент истребитель потребляется.

Это означает, что его внутренний [[GeneratorState]] completed.

Есть ли способ перемещения итерации обратно в начальную точку только без генератора повторного вызова()

Нет. Spec указывает

Как только генератор входит в "завершенное" состояние, он никогда не покидает его, и связанный с ним контекст выполнения никогда не возобновляется. Любое состояние выполнения, связанное с генератором, может быть отброшено в этот момент.

или, возможно, с помощью генератора вызовов(), только с использованием прототипа  или методы конструктора, доступные в итерируемом объекте

Нет. Хотя это явно не указано в спецификации, в итерируемом объекте доступно больше свойств экземпляра, чем [[GeneratorState]] и [[GeneratorContext]].

Однако информативные "отношения объектов генератора" grapic гласят:

Каждая функция генератора имеет связанный прототип, который не имеет свойства конструктора. Следовательно, экземпляр генератора не предоставляет доступ к его генераторной функции.

Я хотел бы иметь возможность передавать итерацию в другую область

Передайте функцию генератора. Или что-то, что дает новые экземпляры генератора.

Ответ 3

Насколько я могу сказать, это невозможно. Per эта полезная вики и черновик версии ES6 для генераторов, как только вы вернетесь из него (а не уступите), он помещает его в состояние "closed", и нет способа вернуть его в состояние "newborn", из которого начинается новый генератор.

Вам может потребоваться передать обратный вызов в другую область для создания нового генератора. В качестве рабочего решения вы даже можете добавить этот обратный вызов как настраиваемый метод для генератора, который вы отправили в другую область, если хотите, и этот обратный вызов создаст новый генератор для другой области.

Если вы думаете о том, как работают генераторы, им придется выполнить с нуля до reset их начальное состояние, и просто нет оснований для его поддержки. Это было бы аналогичным спросить, почему вы не можете просто повторно выполнить конструктор на существующем объекте и ожидать, что в одном и том же объекте будет виргинский объект. В то время как все это технически выполнимо, оно волосатое, чтобы сделать работу правильной, и нет никаких оснований для ее поддержки. Если вы хотите девственный объект, просто создайте новый. То же самое с генератором.


Это немного взломать, но это любопытная штука. Вы могли бы сделать генератор, который повторялся. Предположим, что ваш генератор работал следующим образом:

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

Я могу легко увидеть, как это может быть анти-шаблон, хотя, если вы когда-нибудь это сделаете:

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

У вас будет бесконечный цикл, поэтому его нужно будет использовать с большой осторожностью. FYI, приведенная выше вики показывает примеры бесконечной последовательности Фибоначчи, поэтому бесконечный генератор, безусловно, предполагается.

Ответ 4

В соответствии с черновик версии ES6,

Как только генератор входит в состояние "completed", он никогда не покидает его, и связанный с ним контекст выполнения никогда не возобновляется. Любое состояние выполнения, связанное с генератором, может быть отброшено в этот момент.

Таким образом, он не может reset после завершения. Это также имеет смысл. Мы называем это генератором по какой-то причине:)

Ответ 5

Вы также можете создать свой генератор reset следующим образом:

let iterable = generator();

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

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

//Now the generator has reset the iterable and the iterable is ready to go again.

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

Я лично не знаю плюсов и минусов этого. Просто, что он работает так, как вы ожидаете, переназначив итерацию каждый раз, когда генератор закончит.

РЕДАКТИРОВАТЬ: С более подробными сведениями о том, как эта работа я бы рекомендовал просто использовать генератор, например Azder Показанный:

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

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

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

Рекомендуемая вами версия не позволит вам запускать итерации, если она когда-либо провалится... Например, если вы ждали на одном URL-адресе позвонить другому. Если первый URL-адрес не удался, вам придется обновить приложение до того, как он сможет снова попробовать этот первый выход.

Ответ 6

Всякий раз, когда вам нужно "reset" повторить, просто отбросьте старый и сделайте новый.

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);
}

Ответ 7

Я думаю, что это касается не генератора, а итератора, который на самом деле "делает работу". Чтобы сбросить итерацию, вам просто нужно создать новый итератор. Я бы, вероятно, использовал бы тупую функцию более высокого порядка, как это:

function *foo() {
    yield 1;
    yield 2;
    yield 3;
}

const iterateFromStart = (func) => {
    // every invocation creates a brand new iterator   
    const iterator = func();
    for (let val of iterator) {
        console.log(val)
  }
}

iterateFromStart(foo); // 1 2 3
iterateFromStart(foo); // 1 2 3