ES6 introduced Iterable and Iterator protocols that allow objects to define custom iteration behavior. Any object can become iterable by implementing the [Symbol.iterator] method.

When ES6 launched, it introduced two important protocols Iterable and Iterator. Basically we can convert any type of object to iterable by implementing these two protocols. By converting the object to iterable we can perform looping with for...of loop. We can also use spread operator in array (which is ... symbol in JS) on these objects. First let's understand these two protocols:
As per MDN
The
iterable protocolallows JavaScript objects todefineorcustomizetheiriteration behavior, such as what values are looped over in afor...ofconstruct.
In simple terms it is a rule and by following this rule we can do two things:
iteration behaviouriteration behaviour to it.If you have used JavaScript as a beginner then you have already used one of the famous iterable Arrays. Obviously there are other built in iterables in JavaScript as well. Some of the examples are :
Now the main question comes into picture.
How do we implement this protocol ?
This is very simple. We just need to implement @@iterator. This @@iterator is special property in JS. So to make any object iterable we need to add this @@iterable property to it.
We can get this by using constant symbol [Symbol.iterator]. If you do not known what Symbols are, please read here.
@@iterator should be a simple method with no arguments which will return a value that should comply with iterator protocol.
To summarise, we can convert any object to Iterable by using following steps:
@@iterable property via [Symbol.iterator]@@iterable should be a no argument method@@iterable method should be an iterator.
Before we explore deep into Iterable Jungle. Let's talk about iterator protocol
As per MDN
The
iterator protocoldefines a standard way to produce a sequence of values (either finite or infinite), and potentially a return value when all values have been generated.
In plain English:
shape of values during iteration.no more values when we have gone through all the values.To make any object iterator we need to implement next() method which will return an object which should have these two properties to it:
That's quite simple. Isn't it ? here is an example of Infinite Counter iterator

You can create a finite counter iterator as well

Notice how when limit is reached we return done: true. This is to tell the iterator like for...of loop that there is no more values and you can stop the loop.
Now that we know how to implement iterator, let's head back to our iterable and implement it fully.
So for our example we want user to be iterable and return us [key, value] when we iterate through for...of loop. If you try to iterate user in for...of without implementing iterable you will get following error:
TypeError: user is not iterable

So here is a codesandbox implementation of making user iterable:

As you can see, we have added [Symbol.iterator] which intern return an object containing our next() function which implements iterator protocol.
We can reduce some of code if we use as special kind of function called Generator Functions
Generator Functions are just a syntactic sugar. In our own implementation of iterator function we need to keep track of internal states like value and done. Generator functions returns a special iterator which is called Generator
Generator functions are declared with function* syntax. And it uses a special keyword yield to give us values over course of iteration.
yield is very different from return. When we return from a function, it simply means end of execution and we come out of function. Where as when we yield, generator function pauses the execution and keep track of what to yield next. So when we call next again on our generator it will yield next value in line.
Let's take a look of an example
As you can see when we create instance of our generator it return us an iterator. It does following:
next first time it will yield a {done: false, value: 1} and pause.next again it keeps track of it's state and yield {done: false, value: 2}next as there is nothing to yield any more it gives us {done: true, value: undefined}You can keep on calling next() after it is done, but it will always give you {done: true, value: undefined}.
Now let's use generator for our Infinite Counter
As you can see, with Generator it is a lot cleaner.
You might be thinking, it's all cool. But I do not want to do all this just to make an object Iterable. I have Object.entries I will use that. Give me some good practical example.
So here it is.
I am going to implement very basic LinkedList. It only contain following methods
Main portion of code to check is this:
You can see how I made LinkedList iterable by implementing [Symbol.iterator]. * in front of [Symbol.iterator] makes it a generator and I am yielding the values until whole list is exhausted.
Next portion to look at is toString
You can see here use take advantage of [Spread operator] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_array_literals) in array of Iterable. I spread it in an array then take advantage of toString of Array object.
Recently I got to know that one of Redux libraries Saga uses generators heavily.
Some of example used here can be found in this Codesandbox.
Thank you for reading.