Skip to content

Instantly share code, notes, and snippets.

@marcelbeumer
Created January 21, 2015 11:49
Show Gist options
  • Save marcelbeumer/99c1539ba1e18c5f1702 to your computer and use it in GitHub Desktop.
Save marcelbeumer/99c1539ba1e18c5f1702 to your computer and use it in GitHub Desktop.
title date tags
Generators in JS
2014-08-12 11:10:08 +0200
generators, js, async

Generators are function executions that can be suspended and resumed.

It's supported in Firefox and node 0.11 with --harmony, unclear when it gets to stable. You can also transpile using traceur or regenerator.

Keywords are function*, yield and yield*. API is: {value: <value>, done: <bool>} <generator>.next(value).

Example generator function:

function* channel() {
  console.log('running channel func');
  var name = yield 'hello what is your name';
  return 'well hi ' + name;
}

Calling channel will return a generator, and not execute the function body. Calling .next will execute the function body until the first yield or return statement. The return value is an object that looks like {value: <value>, done: <bool>}. When calling next you can pass an argument that will then be the (direct) return value of the yield in the generator itself.

gen = channel();
gen.next(); // {value: 'hello what is your name', done: false}
gen.next('Marcel'); // {value: 'well hi Marcel', done: true}

Instead of calling .next you can also loop through the values of the generator:

function* iter () {
 for (var i = 0; i < 10; i++) yield i;
}
for (var val of iter()) {
  // val is 0 to 9
}

Note that we use for-of instead for-in, because we want to loop over the values and not over the keys.

Speaking of which, strangely enough we can't iterate over the keys of a generator.

for (val in wrappedIter()) {
  console.log(val); // doesn not get called
}
console.log(wrappedIter().next)); // does work

A function can yield another generator function using the yield* keyword. In a way it's about flattening nested generators.

var val;

function* iter () {
 for (var i = 0; i < 3; i++) yield i;
}

function* wrappedIter () {
  yield* iter(); // yield all yields of this generator
  yield 'done';
}

for (val of wrappedIter()) {
 console.log(val); // 0, 1, 2, 'done'
}

You can throw errors using .throw.

Full examples

Demonstration of basic generator and next:

function* channel() {
  console.log('running channel func');
  var name = yield 'hello what is your name';
  return 'well hi ' + name;
}

var gen;
var iter;

// Get generator. The function body did not get executed yet.
gen = channel();

// Run the function body until the first yield or end of function
iter = gen.next();
console.log(iter.value); // hello what is your name

// Check if we are done (we are not)
console.log('we are', iter.done ? '' : 'not', 'done');
if (!iter.done) {
  iter = gen.next('Marcel');
  console.log(iter.value); // well hi Marcel
}

Follow up

Generators are used in combination with thunks, promises and libraries such as co. Also see the promises, thunks, generators and async in js article.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment