TomLisankie
TomLisankie

Reputation: 3955

Why does my generator become empty after being iterated through?

I have a generator being returned to me by a function call from a library I'm using. I then pass this generator to a function which iterates through it and does a bunch of logic on each of the items. I then want to refer to this same generator after that function has been called. However, it seems the generator no longer has/generates any items. The code is along these lines:

let myGenerator = this.generatorFunc();
console.log(Array.from(myGenerator).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(myGenerator);
console.log(Array.from(myGenerator).length); //now prints 0 when I need it to be N still

iterateThroughGenerator(generator) {
    for(let element of generator) {
        // do a bunch of stuff with element
    }
}

Upvotes: 2

Views: 1172

Answers (4)

Patrick Roberts
Patrick Roberts

Reputation: 52015

Others have already explained why this is happening, but I'll recap anyway.

Generator functions return a generator object, which implements both the iterable protocol (a Symbol.iterator property) using a simple circular reference to itself, and the iterator protocol (a next() method) in order to statefully iterate through the control flow of its generator function.

To fix your problem, encapsulate the generator function call with an object that implements the iterable protocol by returning separate instances of your generator object, and you can use it the same way:

const iterable = { [Symbol.iterator]: () => this.generatorFunc() };

console.log(Array.from(iterable).length); //prints N which is specified elsewhere
this.iterateThroughGenerator(iterable);
console.log(Array.from(iterable).length); //still prints N

iterateThroughGenerator(iterable) {
    for(let element of iterable) {
        // do a bunch of stuff with element
    }
}

Upvotes: 1

junvar
junvar

Reputation: 11604

That's how iterators work. Invoking a generator returns an iterator, which can be iterated once. This is the same in most other languages.

let generator = function* () {
  for (let i = 0; i < 3; i++) 
    yield i;
};

let iterator = generator();

console.log(Array.from(iterator)); // [1...3]
console.log(Array.from(iterator)); // []

console.log(Array.from(generator())); // [1..3]
console.log(Array.from(generator())); // [1..3]

Upvotes: 1

Bergi
Bergi

Reputation: 665574

Array.from() exhausts the generator (iterating it until the end), and iterating it further won't yield any more elements. next() calls will always return {value: undefined, done: true}.

To create a new generator that starts from the beginning again, you need to call the generatorFunc() again.

Upvotes: 0

Nicholas Harder
Nicholas Harder

Reputation: 1558

Once the generator function is completed, then you have to call this.getGeneratorFunc() to recreate the generator again. Also, when you do Array.from(myGenerator), it will complete that generator as well, so when you call this.iterateThroughGenerator(myGenerator) then nothing will occur because there are no elements being returned from the generator anymore. So you can either save the result of the generator into an array and reuse that array, or call this.getGeneratorFunc() three times for each time you want to get elements from it. In this specific example, I would do

 const generated = Array.from(this.getGeneratorFunc());
 console.log(generated.length);
 this.iteratedItems(generated);
 console.log(generated.length);

Check out this answer as well. Previous Answer
Id also read this.

Upvotes: 0

Related Questions