ES6 Generators and Iterators

N

ode.js ( and more recently io.js ) has put javascript as a programming language on a fast path. We have seen more improvements and advancement in the last 2 years that we have seen in the previous 2 decades. ECMAScript version 6 ( ES6 ) or `harmony` bring a lot of interesting and very useful features. One of the more useful, and oddly enough, confusing features is the introduction of generators and iterators.

The idea of generators and iterators are not unique to javascript. In fact that are fairly common in other programming languages. The Python language is very well know for it wide support for generator / iterator support. In fact it ships with module dedicated to creating and dealing with complex iteration patterns.

You'll know a generator when you see one because it introduces a new syntax.

function* once(){  
    yield 1;
}

Iterator -n, noun.

An Iterator is an object that knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. In JavaScript an iterator is an object that provides a next() method which returns the next item in the sequence. This method can optionally raise a StopIteration exception when the sequence is exhausted

You'll notice two new things here, an asterisk (*) on the function keyword and a yield statement. The * denotes that the function is a generator. A generator is a function that, when called, returns an iterator object. An iterator can be thought of as a loop, much like a for or while loop. However, unlike loops, the iterator object only executes when you want it to by invoking it's next method.

function* once(){  
    yield 1;
}

var i = once();

i.next() // {value: 1, done: false}  
i.next() // {value: undefined, done: true}  

As we can see here, calling the generator doesn't execute the code in the function body, but calling the next method on the iterator does execute the function body up to the point it encounters a yield statement, which returns that value. Another important distinction with an iterator is that is does create a closure, so all defined variables are kept intact at each iteration. If you stop an think about that for a second, this allows you to predefine a loop and pass it around to other parts of your application to be continued at a later point in time.

Let use a more concrete example. We can create an infinite loop, commonly referred to as a cycle. We can create a generator that accepts a single array of values and returns an iterator that returns the next value in the array. When then iterator reaches the end of the array, it will start over at the first element of the array.

function* cycle( items ){  
    var idx = -1
    while( true ){
      idx = ( idx + 1 ) % items.length;
      yield items[ idx ];  
    }
}

var iter = cycle( [1,2,3] );

i.next().value // 1  
i.next().value // 2  
i.next().value // 3  
i.next().value // 1  
i.next().value // 2  
i.next().value // 3  

This is pretty simple. We create a while loop that will never end so the iterator will never stop. And at each call of the iterator we yield the remainder of increment the current index and the length of the array. While it were absolutely possible to create iterators with closures and function overloading in previous version of javascript, ES6 makes it much easier to accomplish. Generators open the doors for a vast array of complex application behavior ( think asynchronous recursion ) that would have required a lot of nasty code to pull off. For a fun exercise try re-implementing Python's itertools with javascript generators!

javascript generators es6 interators node.js