Friday, April 25, 2014

Simplified Explanation of Generators in JavaScript

Generators, not yet available in all browsers nice ECMA 6 isn't final yet, are an interesting way to do iterative programming. That is, to generate data on an "as needed" basis. For example, let's say you have a large array to process, maybe reducing it to a smaller array. Before you start using that smaller array, you have to complete the loop that reduces it. With large arrays, you may want to start working with the reduced data as it becomes available, rather then waiting for the entire reduce process to finish.

Think about generators in terms of throughput, and interleaved processing. Here's an example to help you think about these two ideas. First, here's what the generator function itself looks like.

function *youngerThan( age, source ) {
    for ( var i in source ) {
        if ( source[ i ].age < age ) {
            yield source[ i ];
        }
    };
}

Generators have a special asterisk at the beginning of the function name to indicate that it's a generator. The second thing you'll notice is that it yields values, using the yield keyword. Think of yielding as temporarily handing a value, and control, back to the caller till it needs another value. And that's the fundamental premise of generators — pausing the execution of a long running process to increase throughput. The logic inside a generator should be compact and simple, as youngerThan() is. It'll run through the for loop, till it finds an object that has an age that's less than 40. Then, it yields control, and a value, back to the caller. Let's see how this generator is invoked.

function listPeople( gen ) {
    
    var next = gen.next();
    
    if ( next.done ) {
        return;    
    }
    
    listPerson( next.value.name );
    
    setTimeout( function() {
        listPeople( gen );    
    }, 1000 );

}

The listPeople() function is used to take a generator object that yields person objects and render them in the DOM. This works by calling the next() method of the generator to give control back, so it can generate a new value. The call to next() is where we relinquish control back to the generator. If the done property of the yield value is true, it means that there's no more values to generate, and we're done, otherwise, we can render the person, and schedule the next iteration. The reason we're scheduling the next iteration using setTimeout() is to illustrate how the code is interleaved.

Essentially, by waiting for 1 second, we're pausing the for loop in the generator for one second as well.