Be the first user to complete this post

  • 0
Add to List

es6 iterators and iterables - creating custom iterators

This article is the third installment of our Learning ES6 series. In our previous article we explored the different ways in which you could iterate over arrays in ES6. In this article, we will go a step further and explore ways to create your own iterators.


The primary rule for an an iterable

To begin with, lets first see the primary rule that an iterable must follow.
Any object that is iterable must satisfy the following criteria - It should posess an function defined by the name you get by evaluating Symbol.iterator. This function when invoked should return an object which implements a function called next.
That's quite a moutful to say, but its actually pretty straightforward to implement. Before we see an example of a custom iterator, let me show you something really interesting that you can do in ES6 that will help you understand the remaining examples on iterables that will follow.
NOTE: You can try out these es6 examples directly in your browser at http://www.es6fiddle.net/ or https://babeljs.io/repl/.

Dyanmic object keys

In ES6, you can evaluate the keys of objects on the fly. For e.g. Before ES6, if you wanted to define a dynamic property on an object, you had to do it on two separate lines. [wpgist id="c655a6462d9d8876ab3e" file="1_th_es6_custom_iterables.js"] Starting ES6, you can evaluate the key when defining the object itself. [wpgist id="c655a6462d9d8876ab3e" file="2.js"] Now isin't that awesome! Well, to be honest, its not really world changing. But it will help you understand the following example of creating a custom iterable object.

A simple countdown iterable

To demonstrate custom iterables, we are going to create an ordinary javascript object called countdown and turn it into an iterable that countd down from a value of max to 0 using the for-of loop. [wpgist id="c655a6462d9d8876ab3e" file="3.js"] Lets first define a function that actually performs the countdown. We will call this the countdownIterator. All it does is return an object with a next() method such that whenever next() is invoked, it returns an object of the format expected by the for-of loop. [wpgist id="c655a6462d9d8876ab3e" file="4.js"] The next() method of iterables are meant to return an object of the form {value: _somevalue_, done: false} when there is value to be returned and {value: undefined, done: true} when there are no more values to return. In the example above, you can see that only included the non-falsy keys, because the falsy keys are ..... well... falsy. When {done: true} is returned the for-of loop understands that an iterable has been completely drained of all its values. Now lets define our countdown object. Its actually simpler than you might have imagined. [wpgist id="c655a6462d9d8876ab3e" file="5.js"] Remember how we earlier saw dynamic evaluation of object keys? That's exactly what was happening when we did [Symbol.iterator]. The countdown object becomes an iterable object merely because it defines the Symbol.iterator which returns an object that has a next function. And thats it! You can now use the countdown object in regular for-of loops. [wpgist id="c655a6462d9d8876ab3e" file="6.js"] Lets put it all together. When the for-of loop starts it invokes the Symbol.iterator function on the countdown object and gets a reference to an object that has a next() function. It then repeatedly invokes the next() function until it receives an object with {done: false}.

Finishing touches

We could actually make this iterator a little better by defining the iterator function on the countdown object itself. [wpgist id="c655a6462d9d8876ab3e" file="7.js"] Doesn't it look so much more easier now? Apart from the tiny change of using _max the rest of the code is pretty much the same.

Closing unfinished iterators

Last but not the least, iterators can also implement the optional return() method. This method is invoked when a loop fails to drain out all of the values from an iterator, for example due to a break, throw or early return. Think of the return method as a way for the loop to notify the iterator that it will no longer be run to completion. Take a look at the countdown example, which doesn't define a return. [wpgist id="c655a6462d9d8876ab3e" file="8.js"] As you can tell, the countdown continued from where it had last been paused. The return() method can come in handy in situations where it is necessary that you perform some cleanup eventually irrespective of loop completion, e.g when reading a file. In our countdown example, we could use a return method to reset the _max value to undefined as part of doing a cleanup. [wpgist id="c655a6462d9d8876ab3e" file="9.js"] This covers the fundamentals of iterators in es6. In upcoming articles, we will deep dive into es6 generators which seem tricky but are a pretty interesting concept.



Also Read:

  1. querySelector vs getElementById
  2. position: relative
  3. box-sizing
  4. css - align text to an image vertically