Show Buttons
Share On Facebook
Share On Twitter
Share On Google Plus
Share On Linkdin
Share On Reddit
Share On Stumbleupon
Contact us
Hide Buttons

es6 iterators and iterables — creating custom iterators

This arti­cle is the third install­ment of our Learn­ing ES6 series. In our pre­vi­ous arti­cle we explored the dif­fer­ent ways in which you could iter­ate over arrays in ES6. In this arti­cle, we will go a step fur­ther and explore ways to cre­ate your own iterators.


The pri­mary rule for an an iterable

To begin with, lets first see the pri­mary rule that an iter­able must follow.

Any object that is iter­able must sat­isfy the fol­low­ing cri­te­ria — It should posess an func­tion defined by the name you get by eval­u­at­ing Symbol.iterator. This func­tion when invoked should return an object which imple­ments a func­tion called next.

That’s quite a mout­ful to say, but its actu­ally pretty straight­for­ward to imple­ment. Before we see an exam­ple of a cus­tom iter­a­tor, let me show you some­thing really inter­est­ing that you can do in ES6 that will help you under­stand the remain­ing exam­ples on iter­ables that will follow.

NOTE: You can try out these es6 exam­ples directly in your browser at http://www.es6fiddle.net/ or https://babeljs.io/repl/.


Dyan­mic 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 prop­erty on an object, you had to do it on two sep­a­rate lines.

Start­ing ES6, you can eval­u­ate the key when defin­ing the object itself.

Now isin’t that awe­some! Well, to be hon­est, its not really world chang­ing. But it will help you under­stand the fol­low­ing exam­ple of cre­at­ing a cus­tom iter­able object.


A sim­ple count­down iterable

To demon­strate cus­tom iter­ables, we are going to cre­ate an ordi­nary javascript object called countdown and turn it into an iter­able that countd down from a value of max to 0 using the for-of loop.

Lets first define a func­tion that actu­ally per­forms the count­down. We will call this the countdownIterator. All it does is return an object with a next() method such that when­ever next() is invoked, it returns an object of the for­mat expected by the for-of loop.

The next() method of iter­ables 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 val­ues to return. In the exam­ple 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 under­stands that an iter­able has been com­pletely drained of all its values.

Now lets define our count­down object. Its actu­ally sim­pler than you might have imagined.

Remem­ber how we ear­lier saw dynamic eval­u­a­tion of object keys? That’s exactly what was hap­pen­ing when we did [Symbol.iterator]. The countdown object becomes an iter­able 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 count­down object in reg­u­lar for-of loops.

Lets put it all together. When the for-of loop starts it invokes the Symbol.iterator func­tion on the count­down object and gets a ref­er­ence to an object that has a next() func­tion. It then repeat­edly invokes the next() func­tion until it receives an object with {done: false}.

Fin­ish­ing touches

We could actu­ally make this iter­a­tor a lit­tle bet­ter by defin­ing the iter­a­tor func­tion on the count­down object itself.

Doesn’t it look so much more eas­ier now? Apart from the tiny change of using _max the rest of the code is pretty much the same.


Clos­ing unfin­ished iterators

Last but not the least, iter­a­tors can also imple­ment the optional return() method. This method is invoked when a loop fails to drain out all of the val­ues from an iter­a­tor, for exam­ple due to a break, throw or early return. Think of the return method as a way for the loop to notify the iter­a­tor that it will no longer be run to completion.

Take a look at the count­down exam­ple, which doesn’t define a return.

As you can tell, the count­down con­tin­ued from where it had last been paused.

The return() method can come in handy in sit­u­a­tions where it is nec­es­sary that you per­form some cleanup even­tu­ally irre­spec­tive of loop com­ple­tion, e.g when read­ing a file.

In our count­down exam­ple, we could use a return method to reset the _max value to undefined as part of doing a cleanup.

This cov­ers the fun­da­men­tals of iter­a­tors in es6. In upcom­ing arti­cles, we will deep dive into es6 gen­er­a­tors which seem tricky but are a pretty inter­est­ing concept.


Ryan Sukale

Ryan is a UX engineer living in San Francisco, California.

You may also like...