Можно ли сбросить генератор 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);
}

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

6 ответов


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

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

тогда единственное, что вы не должны пытаться сделать, это передать итератор, а не передать генератор:

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

в этот момент Iterable потребляется.

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

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

нет. Спец государств

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

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

нет. Хотя это явно не указано в спецификации, на iterable object чем [[GeneratorState]] и [[GeneratorContext]].

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

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

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

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


на черновик версии ES6,

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

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


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

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

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


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

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, выше wiki показывает примеры бесконечной последовательности Фибоначчи, поэтому, безусловно, рассматривается бесконечный генератор.


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

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

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

EDIT: с большим знанием того, как эта работа я бы рекомендовал просто использовать генератор, как 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-адрес не удается, вам придется обновить приложение, прежде чем он сможет попробовать этот первый выход снова.


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

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