Stop. Iteration time!
Implementing ES6 generators and iterators
You have probably already heard of generators and iterators coming to a browser near you. They have been available in Firefox for a long time and are used extensively all over the Mozilla code base. The V8 team will implement iterators and generators once ES6 has been finalized.
This post describes the current implementation in SpiderMonkey and tries to include the current state of the ES6 draft and discussions.
A simple generator
Let us take a look at a simple example of a generator function that represents an infinite sequence containing all the numbers from 0 to Number.MAX_VALUE. Once it reaches MAX_VALUE it will not increase any further but always return the same number.
Any object of the following shape is an iterator:
The next() method simply returns the item next in the sequence.
Finite sequences
As you surely noticed the generator of the first example produces iterators that will never run out of items. The next example shows an iterator representing a finite sequence:
The given code implements a custom iterator without writing a generator function. Note that it throws StopIteration as soon as it reaches the maximum value to signal that the sequence is exhausted. It is a lot more elegant to implement the same sequence using a generator function:
Generator functions will automatically throw StopIteration when terminating. So how should one consume iterators with finite sequences?
Consuming sequences
In Java for example, you would check iter.hasNext() and stop when it returns false. In JavaScript however you need to use a try…catch statement to catch StopIteration when it is being thrown.
You might wonder if there is a better way to do this and indeed there is. Using for…in or for…of you do not have to catch StopIteration yourself, the JavaScript engine will do it for you. As soon as the sequence is exhausted the loop will terminate normally without the exception being propagated:
StopIteration is special
StopIteration actually is a standard variable that is bound to an object of class StopIteration. It is an ordinary object with no properties of its own and it is not a constructor function.
As StopIteration is a singleton of type StopIteration you can also catch it by checking for equality:
StopIteration is mutable
You should be aware that StopIteration is a mutable global. Just like undefined it can be modified to hold any other value. If you write a library and want to shield against modifications from outside you can use this neat little trick I found on Dave Herman’s blog:
The inner function is a generator that terminates immediately and therefore will throw a StopIteration. The outer function simply catches and returns it.
StopIteration may become a constructor
The current iterator strawman states that StopIteration will become a constructor to maintain compatibility with generator functions returning values.
The equality check from above would not work anymore so it might be better to just use instanceof.
StopIteration may not be part of ES6
The Python way of throwing to denote the end of a sequence is backwards compatible with old ECMAScript versions but there seem to be people not happy with the current proposal. While I can’t tell whether StopIteration is really to be removed a couple of alternative suggestions have been made:
Introduce a keyword to end a frame
To not misuse exceptions for normal control flow ES6 could introduce a stopiteration or endframe keyword that would end the current frame with an optional return value. The obvious downside is that it is probably not backwards compatible.
Add an iterator.hasNext() method
Just like Java the iterator API could consist of the two methods next() and hasNext(). The client would then need to check hasNext() every time before calling next().
Let next() always return an object
Custom iterators would be required to implement a single method but would not need to throw. Instead they would return an object with the property done set to true to indicate that the sequence has ended. The value property would be used to store values passed to yield or return in a generator function.
Food for thought
This is in no way a complete list of possibilities or proposals that were brought up on es-discuss so please make up your own mind about the current iterators implementation and the suggested improvements.
The future is bright
Whether or not StopIteration will end up in ES6, generators and iterators are great and you should make sure to be prepared when they become available in modern browsers as well as on Node.js.
I concentrated particularly on StopIteration but there are lots of great posts out there that go way more into depth about generators and their usage. Make sure to also take a look at libraries like Task.js that combines generators with promises to cooperative tasks.