The following example shows an infinite fibonacci number generator. We then instantiate a generator object and read the first eight values in the sequence.

Let’s do a quick recap of generators (read our primer on generators here) . Generator functions return generator objects when invoked. A generator object has a next method, which returns the next element in the sequence. The next method returns objects with a { value, done } shape.

Iterators follow a similar pattern (you may read our primer on iterators here). They enforce a contract that dictates we should return an object with a next method. That method should return sequence elements following a { value, done } shape. The following example shows a fibonacci iterable that’s a rough equivalent of the generator we were just looking at.

const fibonacci = { [Symbol.iterator]() { let previous = 0 let current = 1 return { next() { const value = current const next = current + previous previous = current current = next return { value, done: false } } } } } const sequence = fibonacci[Symbol.iterator]() console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next()) console .log(sequence.next())

Let’s reiterate. An iterable should return an object with a next method: generator functions do just that. The next method should return objects with a { value, done } shape: generator functions do that too. What happens if we change the fibonacci iterable to use a generator function for its Symbol.iterator property? As it turns out, it just works.

The following example shows the iterable fibonacci object using a generator function for its iterator. Note how that iterator has the exact same contents as the fibonacci generator function we saw earlier. We can use yield , yield* , and all of the semantics found in generator functions hold.

const fibonacci = { * [Symbol.iterator]() { let previous = 0 let current = 1 while ( true ) { yield current const next = current + previous previous = current current = next } } } const g = fibonacci[Symbol.iterator]() console .log(g.next()) console .log(g.next()) console .log(g.next()) console .log(g.next()) console .log(g.next()) console .log(g.next()) console .log(g.next()) console .log(g.next())

Meanwhile, the iterable protocol also holds up. To verify that you might use a construct like for..of , instead of manually creating the generator object. The following example uses for..of and introduces a circuit breaker to prevent an infinite loop from crashing the program.

for ( const value of fibonacci) { console .log(value) if (value > 20 ) { break } }

This was a fun trick. What would you use it for in a real-world program?

Further Reading