Generators are an ES6 feature. Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances. Their primary use case is in representing lazy (possibly infinite) sequences. They return an iterator.

Notably, they are denoted with function* and they use the keyword yield so long as they are values left and pass back objects with a value key, and use return when they are done (and accordingly, the returned object will set done : true).

If you’re in Chrome, go to about:flags and search harmony to enable if you haven’t before.

Overview

An example which creates a fibonacci sequence, with an optional limit:

function* fibonacci(limit){

  /*
  //if ES6 all worked in Chrome (go try Firefox)
  let [prev, curr] = [0, 1];
  for (;;) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
  */

  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    if (limit && current > limit)
      return;
    yield current;
  }
}

var f = fibonacci();

f.next();
//Object {value: 1, done: false}
f.next();
//Object {value: 1, done: false}
f.next();
//Object {value: 2, done: false}
f.next();
//Object {value: 3, done: false}
//and so on...

The cool thing about generators is they allow us to be lazy. So we can pull tricks like this:

for (var num of fibonacci()) {
 if (num < 5000) break
}
console.log(num) // 6765

They can also yield generators (taken from MDN):

function* gen(i){
  while(true){
    yield i++;
    yield* anotherGenerator(i);
  }
}

They can take an initial value

function* acceptor (num) {
  yield 1 * num;
  yield 2 * num;
  yield 3 * num;
}
var a = acceptor(5)
a.next()
//Object {value: 5, done: false}
a.next()
//Object {value: 10, done: false}
a.next()
//Object {value: 15, done: false}
a.next()
//Object {value: undefined, done: true}

And you can pass a value to them at each step:

function* iLoveThings() {
  var one = yield 7; //value passed to next() is assigned to one
  var two = yield one * 5;
  return two * 3;
}
var i = iLoveThings();
i.next(9)
//Object {value: 7, done: false}
i.next(9)
//Object {value: 45, done: false}
i.next(9)
//Object {value: 27, done: true}
i.next(9)
//Object {value: undefined, done: true}

You can also still throw exceptions with throw:

function* catcher() {
  try {
    yield 'foo'
  } catch (ex) {
    return new Error('bar')
  }
}

And you can run them in loops:

function* haiku(){
  yield 'did you know drinking';
  yield 'artificial sweetener';
  yield 'yields diabetes?';
}

for (var number of haiku()){
  console.log(number);
}

With Promises

Using generators and promises together has everyone all excited because we can make asynchronous code look synchonous. The below code uses the async module from node, but you could similarly use Q.async (referenced at bottom).

//imagine we're in node land
function readFile(filename, enc){
  return new Promise(function (fulfill, reject){
    fs.readFile(filename, enc, function (err, res){
      if (err) reject(err)
      else fullfill(res)
    })
  })
}

var readJSON = async(function *(filename){
  return JSON.parse(yield readFile(filename, 'utf8'))
});

//this is beautiful!

var getInSequence = async(function *(){
  var left = yield readJSON('left.json')
  var right = yield readJSON('right.json')
  return {left: left, right: right}
});

var getInParallel = async(function *(){
  var left = readJSON('left.json')
  var right = readJSON('right.json')
  return {left: yield left, right: yield right}
});

I’m excited for generators, because they will allow inexperienced end users of a work-related promise-based API to easily integrate it into their code – the asynchronous hurdle will be largely removed.

Aside from cleaner code, as the web moves toward realtime, being able to streamline workflows with generators will be a boon. I’ll play around with that soon!

Resources

Several examples taken from / inspired by decks below:

Forbes Lindesay: Promises and Generators: control flow utopia - Video

Q, the promise library

Task JS, which brings these two concepts together in a library, but apparently no longer active