ES6 brought a number of new features to the JavaScript language. Two of these features, generators and iterators, have substantially changed how we write specific functions in more complex front-end code.
While they do play nicely with each other, what they actually do can be a little confusing, so let’s check them out.
Iterators
Iteration is a common practice in programming and is usually used to loop over a set of values, either transforming each value, or using or saving it in some way for later.
In JavaScript, we’ve always had for
loops that look like this:
for (var i = 0; i < foo.length; i++){
// do something with i
}
But ES6 gives us an alternative:
for (const i of foo) {
// do something with i
}
This is arguably way cleaner and easier to work with, and reminds me of languages like Python and Ruby. But there’s something else that’s pretty important to note about this new kind of iteration: it allows you to interact with elements of a data set directly.
Imagine that we want to find out if each number in an array is prime or not. We could do this by coming up with a function that does exactly that. It might look like this:
function isPrime(number) {
if (number < 2) {
return false;
} else if (number === 2) {
return true;
}
for (var i = 2; i < number; i++) {
if (number % i === 0) {
return false;
break;
}
}
return true;
}
Not the best in the world, but it works. The next step would be to loop over our list of numbers and check whether each one is prime with our shiny new function. It’s pretty straightforward:
var possiblePrimes = [73, 6, 90, 19, 15];
var confirmedPrimes = [];
for (var i = 0; i < possiblePrimes.length; i++) {
if (isPrime(possiblePrimes[i])) {
confirmedPrimes.push(possiblePrimes[i]);
}
}
// confirmedPrimes is now [73, 19]
Again, it works, but it’s clunky and that clunkiness is largely down to the way JavaScript handles for
loops. With ES6, though, we’re given an almost Pythonic option in the new iterator. So the previous for
loop could be written like this:
const possiblePrimes = [73, 6, 90, 19, 15];
const confirmedPrimes = [];
for (const i of possiblePrimes){
if ( isPrime(i) ){
confirmedPrimes.push(i);
}
}
// confirmedPrimes is now [73, 19]
This is far cleaner, but the most striking bit of this is the for
loop. The variable i
now represents the actual item in the array called possiblePrimes
. So, we don’t have to call it by index anymore. This means that instead of calling possiblePrimes[i]
in the loop, we can just call i
.
Behind the scenes, this kind of iteration is making use of ES6’s bright and shiny Symbol.iterator() method. This bad boy is in charge of describing the iteration and, when called, returns a JavaScript object containing the next value in the loop and a done
key that is either true
or false
depending on whether or not the loop is finished.
In case you’re interested in this sort of detail, you can read more about it on this fantastic blog post titled Iterators gonna iterate by Jake Archibald. It’ll also give you a good idea of what’s going on under the hood when we dive into the other side of this article: generators.
Generators
Generators, also called “iterator factories”, are a new type of JavaScript function that creates specific iterations. They give you special, self-defined ways to loop over stuff.
Okay, so what does all that mean? Let’s look at an example. Let’s say that we want a function that will give us the next prime number every time we call it. Again, we’ll use our isPrime
function from before to check if a number is prime:
function* getNextPrime() {
let nextNumber = 2;
while (true) {
if (isPrime(nextNumber)) {
yield nextNumber;
}
nextNumber++;
}
}
If you’re used to JavaScript, some of this stuff will look a bit like voodoo, but it’s actually not too bad. We have that strange asterisk after the keyword function
, but all this does is to tell JavaScript that we’re defining a generator.
The other funky bit would be the yield
keyword. This is actually what a generator spits out when you call it. It’s roughly equivalent to return
, but it keeps the state of the function instead of rerunning everything whenever you call it. It “remembers” its place while running, so the next time you call it, it carries on where it left off.
This means that we can do this:
const nextPrime = getNextPrime();
And then call nextPrime
whenever we want to obtain — you guessed it — the next prime:
console.log(nextPrime.next().value); // 2
console.log(nextPrime.next().value); // 3
console.log(nextPrime.next().value); // 5
console.log(nextPrime.next().value); // 7
You can also just call nextPrime.next()
, which is useful in situations where your generator isn’t infinite, because it returns an object like this:
console.log(nextPrime.next());
// {value: 2, done: false}
Here, that done
key tells you whether or not the function has completed its task. In our case, our function will never finish, and could theoretically give us all prime numbers up to infinity (if we had that much computer memory, of course).
Cool, so Can I Use Generators and Iterators Now?
Although ECMAScript 2015 has been finalized and has been in the wild for some years, browser support for its features — particularly generators — is far from complete. If you really want to use these and other modern features, you can check out transpilers like Babel and Traceur, which will convert your ECMAScript 2015 code into its equivalent (where possible) ECMAScript 5 code.
There are also many online editors with support for ECMAScript 2015, or that specifically focus on it, particularly Facebook’s Regenerator and JS Bin. If you’re just looking to play around and get a feel for how JavaScript can now be written , those are worth a look.
Conclusions
IGenerators and iterators give us quite a lot of new flexibility in our approach to JavaScript problems. Iterators allow us a more Pythonic way of writing for
loops, which means our code will look cleaner and be easier to read.
Generator functions give us the ability to write functions that remember where they were when you last saw them, and can pick up where they left off. They can also be infinite in terms of how much they actually remember, which can come in really handy in certain situations.
Support for these generators and iterators is good. They’re supported in Node and all modern browsers, with the exception of Internet Explorer. If you need to support older browsers, your best bet is to use a transpiler such as Babel.
Frequently Asked Questions (FAQs) about ECMAScript 2015 Generators and Iterators
What is the difference between iterators and generators in ECMAScript 2015?
Iterators and generators are both features of ECMAScript 2015 that deal with data streams. An iterator is an object that allows a programmer to traverse through all the elements in a collection. It has a next() method that returns the next item in the sequence. On the other hand, a generator is a function that can stop midway and then continue from where it stopped. In other words, a generator appears to be a function but it behaves like an iterator.
How do I use the yield keyword in ECMAScript 2015?
The yield keyword is used in ECMAScript 2015 to pause and resume a generator function (function* or legacy generator function). Yield can return a value from a generator function. This returned value is typically an object with two properties: value and done. The value property is the result of evaluating the yield expression, and done is a boolean indicating whether the generator has yielded its last value.
What is the purpose of the next() method in ECMAScript 2015?
The next() method is a crucial part of the iterator protocol in ECMAScript 2015. It returns an object with two properties: value and done. The value property is the next value in the iteration sequence, and done is a boolean indicating whether the iteration is complete. If done is true, then the iterator is past the end of the iteration sequence.
How can I use generators for asynchronous programming in ECMAScript 2015?
Generators in ECMAScript 2015 can be used to simplify asynchronous programming. They can be used to block execution in order to wait for asynchronous operations to complete, without blocking the entire program. This can make asynchronous code look and behave a bit more like synchronous code, which can be easier to understand and reason about.
What is the difference between a for…of loop and a for…in loop in ECMAScript 2015?
The for…of loop in ECMAScript 2015 is used to loop over iterable objects, like arrays, strings, maps, sets, and so on. It invokes a custom iteration hook with statements to be executed for the value of each distinct property. On the other hand, the for…in loop is used to loop over the properties of an object. It returns a list of keys on the object being iterated.
How can I create a custom iterator in ECMAScript 2015?
In ECMAScript 2015, you can create a custom iterator by defining an object that has a next() method. This method should return an object with two properties: value and done. The value property is the next value in the iteration sequence, and done is a boolean indicating whether the iteration is complete.
What is the role of the Symbol.iterator in ECMAScript 2015?
The Symbol.iterator is a special built-in symbol in ECMAScript 2015. It is used to specify the default iterator for an object. When an object needs to be iterated (such as at the beginning of a for…of loop), its @@iterator method is called with no arguments, and the returned iterator is used to obtain the values to be iterated.
Can you provide an example of a generator function in ECMAScript 2015?
Sure, here’s a simple example of a generator function in ECMAScript 2015:function* idMaker() {
var index = 0;
while (true)
yield index++;
}
var gen = idMaker(); // "Generator { }"
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
In this example, the idMaker function is a generator that produces a sequence of numbers.
How can I use the throw() method with generators in ECMAScript 2015?
The throw() method in ECMAScript 2015 can be used with generators to resume the generator function’s execution and throw an error from the yield expression. The throw() method can be used to handle errors that occur during the execution of a generator function.
What is the significance of the done property in ECMAScript 2015 iterators?
The done property is a boolean that is returned by an iterator in ECMAScript 2015. It indicates whether or not the iterator has any more values to return. If done is true, then the iterator is past the end of the iteration sequence. If done is false, then the iterator can still produce more values.
Byron Houwens is a designer and developer who enjoys focusing on front end technologies and UX. He currently works as a front end developer in the burgeoning edtech environment, and lives in sunny Cape Town, South Africa.