Iterators and Generators in Javascript

Meet Patel
Simform Engineering
5 min readJul 17, 2023

A Comprehensive Guide to JavaScript’s Iteration Mechanisms.

In the world of JavaScript, Iterators and Generator Functions are powerful tools that empower developers to have control over data iteration and flow. Together, these functions form an impressive duo that enables us to write concise, efficient, and flexible JavaScript code.

Iterator and generator functions are advanced concepts in JavaScript that allow for efficient and customizable looping through data structures. They also provide a mechanism for customizing the behavior of for…of loops.

Let’s understand what exactly iterator and generator functions entail.

Iterators

In JavaScript, an iterator function is a unique function that returns an iterator object. An iterator object is an object that has a next() method, which returns an object with two properties: value and done. The value property represents the next value in the sequence, and the done property indicates whether the iterator has reached the end of the sequence.

Iterator functions can be used to iterate over collections of data, such as arrays or objects.

Here is an example of an iterator function that iterates over an array:

The above code defines a function called Iterator that takes an array as an argument and returns an iterator object. The iterator object has a method called next, which returns the next item in the array and updates an internal nextIndex variable to keep track of the array index.

The next method checks whether nextIndex is less than the length of the array. If it is, the method returns an object with the value of the array at the nextIndex position and sets the done property to false. The nextIndex variable is then incremented by one. If nextIndex is greater than or equal to the array length, next sets the done property to true.

The code then defines an array of numbers [1, 2, 3, 4, 5] and creates an iterator object from it using the iterator function. The arrayValue variable is assigned to the iterator object.

The code then calls the next method on the arrayValue iterator object multiple times, logging the returned objects to the console. Each time the next method is called, it returns an object with either the value of the next item in the array or a done property of true, indicating that there are no more items in the array.

The first few calls to next will log the values [1, 2, 3, 4, 5], and the subsequent calls will log objects with a done property of true and a value of undefined.

But hold on… we can directly use the Symbol.iterator function to iterate over an array.

Let’s see an example of it:

Here, we can see that the Symbol.iterator is a function that returns the next function, which is the same as the function that we had made earlier🤩.

Generators

A generator function is a special type of function that can be used to control the flow of execution by yielding values one at a time instead of returning them all at once. When a generator function is called, it does not execute immediately but instead returns a generator object that can be used to control the execution of the function.

The generator object has a next() method that can be used to resume execution of the function, and each time the function encounters a yield statement, it returns the yielded value and suspends execution until the next() method is called.

Generator functions are useful for creating iterators and writing asynchronous code using async/await syntax. They allow you to write code that looks like synchronous code but executes asynchronously in the background.

Generator functions are declared using the function* syntax, which is similar to regular function syntax but with an asterisk (*) after the function keyword.

Here's an example of a simple generator function that yields the numbers 1, 2, and 3:

Here, when calling the myGenerator() function, which returns a generator object, the next() method can be called on the generator object to resume execution of the generator function and return the next yielded value.

Each call to next() resumes execution of the generator function from the point at which it was paused by the last yield statement. When the generator function has no more values to yield, it returns { value: undefined, done: true }.

Let’s understand it with another example of the Fibonacci series.

In this example, we define a generator function called fibonacciGenerator(). It uses the yield keyword to return the current Fibonacci number and then continues execution from where it left off.

Inside the generator function, we maintain two variables: current and next. We start with the current set to 0 and next set to 1, representing the first two Fibonacci numbers.

The generator enters an infinite loop using while(true). In each iteration, it yields the current Fibonacci number using yield current and then calculates the next Fibonacci number by adding current and next. Finally, it updates the values of current and next using destructuring assignment: [current, next] = [next, current + next].

To use the generator, we create an instance of it using const fibonacci = fibonacciGenerator(). Then we can call fibonacci.next().value to get the next Fibonacci number in the sequence.

Here, we generate the first 10 Fibonacci numbers by calling fibonacci.next().value in a loop and printing the result to the console.

Advantages of Iterator and Generator Functions

Iterators and generator functions offer several advantages. Here are some key benefits:

1. Iterating over collections

  • Iterators and generator functions provide a standardized way to iterate over collections of data, such as arrays, sets, maps, or custom data structures.
  • They allow you to access each element in a sequence one at a time, simplifying the process of working with collections.

2. Lazy Evaluation

  • Both iterators and generator functions enable lazy evaluation, which means that the data is generated or fetched on demand as it is iterated over.
  • This can greatly reduce memory usage and processing overhead, especially when working with large datasets, as it allows you to process data one piece at a time instead of loading the entire dataset into memory.

3. Code Reusability

  • Both iterators and generator functions promote code reusability as they provide a consistent way to iterate over different types of data collections.
  • Once you’ve implemented an iterator or generator function for a specific type of data, you can reuse it in multiple parts of your codebase without having to duplicate the iteration logic.

4. Asynchronous Iteration

  • Generator functions can be used to implement asynchronous iteration, allowing you to work with asynchronous data sources, such as APIs or streams, in a more streamlined way.
  • This can simplify your asynchronous code and make it easier to handle errors and manage resources.

5. Improves Performance

  • Using iterator and generator functions often leads to improved performance compared to traditional approaches, especially when working with large datasets.
  • Lazy evaluation and customizable iteration logic can help optimize memory usage and processing time, resulting in faster and more efficient code.

Conclusion

Iterator and Generator functions help you fetch and process data efficiently, with flexibility, reusability, and improved performance, making your codebase more manageable and scalable. Just like how a well-organized kitchen helps a chef create delicious meals, Iterator and Generator functions are essential tools for modern JavaScript developers to handle data processing requirements effectively.

For more updates on the latest tools and technologies, follow the Simform Engineering blog.

Follow Us: Twitter | LinkedIn

--

--