Iteration Protocol in JavaScript-Part 2

Sunilkathuria
In Computing World
Published in
6 min readJun 3, 2024

In the previous article, we learned to implement iterators and iterables. In this article, we will continue learning about generators, which are part of the iteration protocol.

Generators are powerful programming tools that simplify the implementation of iterators, enhance code readability, and reduce the likelihood of errors. Moreover, they possess the unique ability to pause a function’s execution.

Understanding Generators

function*

We can declare a generator function like a regular function with an asterisk “*” right after the function keyword.

Generator object

When the function myGenerator() executes, it does not start the function’s execution immediately. Instead, it returns a Generator object. It is a special type of iterator object that implements the next() method. Refer to the following code snippet.

yield keyword

Whenever the next() method is called, it executes the generator function until it executes the yield statement. When this statement executes, it pauses the execution of the generator function and gives back (or yields) the value to the caller. The generator object maintains its state between the two next() calls.

Implementing a Generator function

There are different ways in which we can implement a generator in JavaScript. These are generator functions, as an expression, as an attribute of an object, and as a method of a class. This section shows all these implementations.

As a generator function

This generator function implements a custom counter that accepts a number as an argument and gives out a count of “number” starting from 0. The code snippet below creates a counter of 5 numbers starting from 0. The output of the code snippet below is 0, 1, 2, 3, 4.

Note: the return statement completes the function execution and removes it from the stack.

This is another example of a generator function that helps you generate a range of numbers and iterate through them.

As a function* expression

When you want to write an unnamed generator function, you use the function* expression and create an anonymous function. We are implementing a simple counter as a function expression in the code snippet below.

Note: both the generator function and generator expression use the similar syntax of “function *”. The generator function has a name, whereas the generator expression creates an anonymous function, which can be assigned to a variable.

As an attribute in an object

It is also possible to define a Generator function as an attribute in an object. We can use this generator to define customized iteration. The following code snippet implements a counter.

As a method of class

Just like a function of an object, we can write a generator function as a method of a class. You have to prefix a generator function with an asterisk “*”. You can also give any name to a generator function. The following code snippet explains the same.

Using generators with user objects

Implementing iterable iterator using a generator

Like in the previous article, we will create a collection of books using a linked list. The linked list can only add a new book (node) at the end. We will implement a generator that iterates through this collection of books.

The class Book acts as a node of the linked list. It stores the book, the author’s name, and a reference to the next book.

The object myBookCollection maintains the linked list and provides the functionality of adding a new book.

It implements the method [Symbol.iterator], which implements a generator function. Refer to the code snippet below. It is an iterable iterator implementation where an iterable protocol implements a generator function.

Output of the above test code

Another way of implementing [Symbol.iterator] is

Using Iteration protocol

The iteration protocol is implemented outside the book collection object using generators in this implementation.

Output of the above test code

yield — some more

So far, we have seen a very simple implementation of the generator function where we traverse several data elements at the same level.
Now, we change the storage of the books based on genre.
We have a list of genres and maintain a list of books under each genre. In this situation, the above implementation is of little help. JavaScript provides a concept of nested delegation. As the name suggests, a generator can delegate its operation to another generator, allowing multi-level iteration. The following image gives a better understanding of new storage.

For this, we use the expression yield*. When yield* is mentioned along with a function name (as an operand), it starts iterating over the operand and yields value(s). It delegates the iteration process from the generator to the iterator (function mentioned as an operand). It behaves the same way a normal generator behaves; once “done” is set to true or the operand returns, the control returns to the main generator. Refer to the following code snippet.

Collection of books as per genre

With this, we complete this series on iteration protocols.

Summary

In this article, we learned…

  • The concept of generators is how they pause the execution of a function.
  • The purpose of keywords like “function *” is to help define a generator function. Also learned about declaring a generator function both in an object and a class.
  • Concept of the “generator object”, and “yield” and “yield*” statements. How the two flavors of yield behave.
  • Implemented generator objects to get a good grasp of the concept.

References

GitHub - Source Code

Did you enjoy this article? Please clap it and share it with your friends.
If you have found this article helpful, please subscribe to read more articles.
Feel free to comment!!

--

--