For more tips like this, sign up to the weekly newsletter!

Wrap generators in iterables

Generator functions provide an easy way to write iterators without the protocol boilerplate. But unfortunately, they are also an iterable that returns itself.

For one-shot generators, it's fine. But this construct prevents multiple iterations over it.

Consider the following function, that gets an iterable and iterates over it twice:

const gen = function*() {
  yield* [1, 2, 3];
}

const printTwice = (it) => {
  console.log(Array.from(it));
  console.log(Array.from(it));
};

printTwice(gen());

This prints:

[1, 2, 3]
[ ]

The second iteration returns nothing. Contrast that with a traditional Array, which is also an iterable:

printTwice([1, 2, 3]);
[1, 2, 3]
[1, 2, 3]

Multiple iterations are possible, as expected.

Wrap the generator in an iterable

To allow multiple iterations, wrap the generator function in an iterable that creates a new instance every time:

const wrapGenerator = (gen) => (...args) => ({
  [Symbol.iterator]: () => gen(...args)
});

const wrappedGenerator = wrapGenerator(gen);

printTwice(wrappedGenerator());

This wrapped generator supports multiple iterations, and behaves like a normal iterable:

[1, 2, 3]
[1, 2, 3]
Try it
Learn more: