Iterators & Generators , how they work ?
I had that long curiosity in my mind to understand Iterators and Generators in depth. How they actually work ?. I dived into it and understood a lot of things. I am writing this article to share my thoughts and understanding about iterators and generators. And guys I may be wrong in my understanding. Feel free to comment and let me know, if there is any discrepancy
What will be the course of action in this article ?
We will be implementing a simple iterator function.
Then we will jump into generators and how they are using yield and iterator to give us a wonderful flow of execution. So, Let’s start
Lets say we have an array. we will be using this array to depict the iterator behaviour. Iterator basically simulates the data flow. You get the next element in iterable just by calling next function. Iterators uses closure to keep the current state of iterating iterable. I know there are many iterable words I have used, but hold on you will understand in a while.
Iterables: These are the data structures which can iterate through and apply some logic on each elements in the list. e.g Array, Object, String, Map, Set
Iterator: It is the function that we are going to write to give us next function, which when invoked will give us the next value.
Iterating: It is act of walking through all the elements one by one.
So on line 11, it variable is assigned with a function which has access to nextIndex. So every time you call .next(), nextIndex value is getting incremented by 1 until you reaches the index === array.length. You will notice done set as false when we reach the array.length + 1 index. That is your iterator, which give you the interface to get the next value at every call. we will see further in the article how this functionality is used in generators
yield keyword can be think of return. It stop execution of generator function , but if you have iterator returned by generator in the scope. You can again go back inside the generator context. Ok, I think we need code here for a better understanding
So here we have defined a simple generator. generator are special functions with * appended to function keyword. You call generator in a similar way as you call functions. Generators can take arguments similiar to function. See line 12 , 23 is passed as an argument.
Generator allows you to pause and play the execution of itself. Now you can stop the execution of a function, do some other stuff, pass the calculated value to the function and continue the execution context.
- Line 12. Called generator to get the iterator instance. which will be used to play the code. Remember it is same as iterator object that we explained above in the iterator section. Till now nothing is consoled log. we will call TICK position as the current line in myGenerator , where code is being executed. TICK has not started.
- Line 13. Called next of iterator stored in line 12. Code between line 2 to line 4 will be executed. After that yield keyword is encountered. It stops the execution throw out of the generator. Now the TICK position is at line 4. value 24 will be consoled log at line 13.
- Line 14: Called .next again. Current TICK position is 4. So the code will start executing from line 5. On line 5 a const is decided. On line 6 , again the execution is paused and value 2 is returned. value 2 will be consoled log at line 14. TICK position is at line 6. value of t variable is undefined until now.
- Line 15: Called .next again. Current TICK position is 6. In the line 15 call, we are passing 14 as the arguments which will be assigned to variable t in myGenerator on line 6. Line 7 execution will happen consoling value 14. On line 8 , execution is stopped and returned back to line number 15. TICK position is now at line 8. value 4 is returned as from yield hence consoling 4.
- Line 16. Called .next again. Current TICK position is 8. now line 9 will be executed and again thrown out of generator. Now consoling 5. Now TICK position is now at line 10.
- Line 17, Called .next again. Current TICK position is 10. now as the function is over ,undefined is received and consoled log on line 17
This is how generators and iterators work together. You must appreciate the fact that how we are able to pause and play passing in our desired arguments.Iterators are very good at processing a stream of data. when generators are paused they are not blocking callback queue until the yield value is asynchronous task. Here is the working link of the code explained above:
Same way , we can implement asynchronous generator function. Here is the one of the example
Observe Line 3 , how we are entering into getRandomText generator again by calling myRandomIterator.next(value) . Notice we have also passed in the value which will be printed on line no. 13. Guess the sequence of console logs before you tun the code below. Here is the code pen embed.
Many useful flows can be written using iterators and generators. where you want to pass in the data in the midst of function execution. @redux-saga is all based on it. They allow us to write short and concise code. Using generators and iterators can allow you to write asynchronous code in a synchronous manner. In the next articles , we will see how async await (ES 7) can make your life more easier. You won’t need to call .next to enter into function execution how we did it on line 3.
Thanks everyone. I would like to discuss more on generators and iterators use case. Please comment below, if you find any discrepancy or confusion. I would be more than happy to discuss that. If you like this article, hit applause, comment or share.