Be the first user to complete this post
|
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.
- Part 1: Getting started with ES6 modules
- Part 2: Getting started with ES6 iterators and iterables
- Part 3: ES6 iterators and iterables - creating custom iterators - THIS ARTICLE
- Part 4: Getting started with ES6 generator functions
- Part 5: Generators and Yield in ES6
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 evaluatingThat'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.Symbol.iterator
. This function when invoked should return an object which implements a function callednext
.
NOTE: You can try out these es6 examples directly in your browser athttp://www.es6fiddle.net/
orhttps://babeljs.io/repl/
.
Dyanmic object keys
In ES6, you canevaluate
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 calledcountdown
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 areturn
.
[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:
- nodejs: generate uuid / guid
- center using css
- window vs document
- css specificity interview question
- position: relative