Hidden Variables

Domenic's blog about coding and stuff

ES6 Iterators, Generators, and Iterables

| Comments

I wrote up a quick guide to the terminology around ES6’s iteration-related concepts, plus some notes and other resources.


An iterator is an object with a next method that returns { done, value } tuples.

An iterable is an object which has an internal method, written in the current ES6 draft specs as obj[@@iterator](), that returns an iterator.

A generator is a specific type of iterator whose next results are determined by the behavior of its corresponding generator function. Generators also have a throw method, and their next method takes a parameter.

A generator function is a special type of function that acts as a constructor for generators. Generator function bodies can use the contextual keyword yield, and you can send values or exceptions into the body, at the points where yield appears, via the constructed generator’s next and throw methods. Generator functions are written with function* syntax.

A generator comprehension is a shorthand expression for creating generators, e.g. (for (x of a) for (y of b) x * y).



The new for-of loop works on iterables, i.e. you do for (let x of iterable) { /* ... */ }. So for example, it works on arrays by looking up their Array.prototype[@@iterator]() internal method, which is specified to return an iterator that does what you’d expect. Similarly Map.prototype, Set.prototype, and others all have @@iterator methods that help them work with for-of and other constructs in the language that consume iterators.

Note that for-in has nothing to do with iterables, or indeed any of the concepts discussed here. It still works as it did before, looping through enumerable object properties, and it will be pretty useless when given an iterable of any sort.

Iterable Iterators

An iterator can also be iterable if it has an @@iterator() internal method. Most iterators in the ES6 draft spec are also iterable, with the internal method just returning this. In particular, all generators created via generator functions or generator comprehensions have this behavior. So you can do:

const lazySequence = (for (x of a) for (y of b) x * y);
for (let z of lazySequence) {

Making Iterable Objects

To make a custom iterable object, you use the Symbol.iterator symbol, which is the inside-JavaScript way of referring to the specification’s @@iterator. It should return an iterator, thus making your object iterable. The easiest way to write the iterator-returning method is to use generator syntax. Putting this all together, it looks like

const iterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;

for (let x of iterable) {

Generator Comprehension Desugaring

You can think of generator comprehensions as “sugar” for writing out and immediately invoking a generator function, with yields inserted implicitly at certain points. For example, the comprehension (for (x of a) for (y of b) x * y) desugars to

(function* () {
  for (x of a) {
    for (y of b) {
      yield x * y;

A Weirdness

It’s not entirely clear why generator comprehensions create generators instead of simple iterable-iterators. In particular, as you can see from the above desugaring, calling throw or giving a parameter to next is pretty useless:

const g = (for (x of [1, 2, 3]) x * x);

g.next();                   // returns { done: false, value: 1 }
g.next(5);                  // returns { done: false, value: 2 }
g.throw(new Error("boo!")); // immediately throws the error

It seems the arguments in favor of generators instead of iterable-iterators are largely that it makes implementers' jobs easier, at least according to this es-discuss thread I started.

Implementation Status

Due to the tireless work of Andy Wingo, V8 (Chrome) has support for generator functions, behind the --harmony flag (or “Experimental JavaScript Features” in chrome://flags). It also has some form of for-of, which only works with generators and with the custom iterables returned by Array.prototype.values and Array.prototype.keys, but does not work with anything else (e.g. arrays themselves). I assume this is because the iteration protocol has not been implemented yet. V8 does not have generator comprehensions.

SpiderMonkey (Firefox) has an old version of everything here, with outdated syntax and semantics. Over the last week or so, Andy has submitted patches to update their generator implementation. He’s now working on for-of and the iteration protocol in bug #907077; no word on generator comprehensions. Note that SpiderMonkey, unlike V8, does not hide these features behind a default-off flag.

Chakra (Internet Explorer) is as always a complete mystery, with no transparency into their development cycle or even priority list.

And I still haven’t forgiven JavaScriptCore (Safari) for its long period of forgetting to implement Function.prototype.bind, so I haven’t even tried looking into their status.