Javascript Iterators and Generators
Javascript became the most popular language in the recent decade. In addition, every day there are a bunch of updates, not only for the syntax, but also in the functional approach. For instance, the concept of using generators instead of traditional iterators. The more the updates, the more powerful your code has become. In this post, I will demonstrate how the generators are working and why it is important to get used to it.
Iterators
According to MDN documentation, iterator In JavaScript is an object that provides a next() method which returns the next item in the sequence. In the following snippet, let’s discover how the javascript work behind the scene for the normal iterator function.
function createLoop(array) { let i = 0; function iterator() { const ele = array[i]; i++; return ele; }return iterator;}const returnNextEle = createLoop([1,2,3]);const ele1 = returnNextEle(); // 1const ele2 = returnNextEle(); // 2
What happened is that the engine reads the block line by line. Each function declaration takes place in memory as a closed box — Javascript doesn’t open that box until function invocation. Hence, constants array, i, and iterator function stored in the global memory of the global execution context. Then, when hit the return keyword the engine exit that execution context and do the following:
1. Open the child function, iterator( ), and begin a new execution context.
2. Push iterator( ) function to the call stack.
Note that by hitting return, we cannot go back to the createLoop( ) execution context — it is gone. However, iterator( ) will have access to the createLoop( ) variable array and i thanks to closure and lexical scope.
Now, iterator( ) will run as long as we invoke the iterator( ). In the example case, the iterator( ) will run twice — because we invoke the function two times only.
So, we can think of this type of iterators as a valve, only open and give the value when invoked. From performance point of view, it is like a thunderstorm change to all the future iterations.
Generators
A generator function is a special type of function that works as a factory for iterators. When it is executed it returns a new generator object. A function becomes a generator function if it uses the function* syntax — MDN.
Generators provide a dynamic flow of data to us, let’s see an example.
function createFlow(array) { let i = 0; const iterator = { next: function() { const ele = array[i] i++ return ele; } }return iterator;}const returnNextEle = createFlow([1, 2, 3]);const ele1 = returnNextEle.next(); // 1const ele2 = returnNextEle.next(); // 2
Javascript’s built-in iterators are in fact objects with a next method that when called returns the next element from the flow/collection.
Dynamic flow
Now, javascript let us make our flow more dynamic using the star *function and yield keyword.
function *createFlow() { yield 1 yield 2}const returnNextEle = createFlow();const ele1 = returnNextEle.next(); // { value: 1, done: false }const ele2 = returnNextEle.next(); // { value: 2, done: false }
Yield keyword is like return; however, the yield can keep you in the execution context. It runs once, and the next time when you invoke the same function, it moves on to the next yield statement. The yield has two properties, value — which returns the value, and done with boolean value — which indicates whether the function is complete or not. You can use return statement with the yield statement in the generator function, but once you hit the return statement, you exit the execution context of the generator.
By hitting return statement, you exit the generator — generator done it’s job.
function *createFlow() { yield 1 return 3 yield 2}const returnNextEle = createFlow();const ele1 = returnNextEle.next(); // { value: 1, done: false }const ele2 = returnNextEle.next(); // { value: 3, done: true }
The power of yield
Yield with the asterisk * can authorize it’s work to another generator. This way you can chain as many generators as you want.
function * yieldGenerator(i) { yield i * 5; yield i + 3;}function * generator(i) { yield * yieldGenerator(i);}const power = generator(5);power.next().value; // 25power.next().value; // 8
Conclusion
Hope this post gives you a clear idea of what is the difference between generators and iterators. The way to implement them in your code to enhance both readability and performance. Thanks for reading!!