header banner
Default

Generators in JS: Everything You Need to Know


Table of Contents

Pallavi Ganpat Babar

What is Generators?

Generators are a special type of function in JavaScript that can be aused and resumed. Unlike regular functions, which run to completion every time they are called, Generators can yield values and then pause their execution until they are explicitly resumed. Generators are a strong tool for building iterators that may be utilised in a variety of contexts, such as iterating through large datasets or handling asynchronous operations, due to their ability to halt and resume execution.

Generator Function Syntax:

A Generator is created by defining a special sort of function known as a Generator Function. In order to build a Generator Function, an asterisk (*) must be included after the function keyword. The following is a basic Generator Function:

function* myGenerator() {
// Generator function code here
}

Yielding Values:

The key feature of Generators is their ability to yield values using the yield keyword. When a Generator encounters a yield statement, it pauses its execution and returns the yielded value. The Generator's state is saved, allowing you to resume its execution from where it left off.

Let’s use a straightforward example to demonstrate this:

function* fibonacciGenerator() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}

const fibonacciSequence = fibonacciGenerator();

// Generate the first 10 Fibonacci numbers
for (let i = 0; i < 10; i++) {
console.log(fibonacciSequence.next().value);
}

In this example, we’ve defined a generator function fibonacciGenerator that generates an infinite sequence of Fibonacci numbers. Here's how it works:

  1. function* is used to declare a generator function.
  2. Inside the generator function, we initialize two variables a and b to the first two Fibonacci numbers (0 and 1).
  3. We enter an infinite loop using while (true) to keep generating Fibonacci numbers.
  4. The yield keyword is used to yield (return) the current value of a as part of the sequence.
  5. We then update a and b to the next Fibonacci numbers using destructuring assignment.
  6. The for loop generates and prints the first 10 Fibonacci numbers by calling fibonacciSequence.next().value.

Output :

0
1
1
2
3
5
8
13
21
34

Generators in JavaScript are a powerful feature with various fascinating hidden capabilities and use cases beyond their basic functionality. Here are some intriguing and secret generator aspects:

1. Two-way Communication:

Generators allow for two-way communication between the caller and the generator function. You can not only send values into the generator using generator.next(value), but you can also receive values from the generator using yield.

function* twoWayGenerator() {
const value = yield "Please provide a value:";
console.log("You entered:", value);
}

const gen = twoWayGenerator();
console.log(gen.next().value); // Outputs: "Please provide a value:"
gen.next("Hello!"); // Outputs: "You entered: Hello!"

  1. The code defines a generator function called twoWayGenerator. This generator yields the message "Please provide a value:" and pauses, waiting for input.
  2. When we create an instance of this generator and call gen.next().value, it starts the generator and logs the message.
  3. Then, by calling gen.next("Hello!"), we send "Hello!" back into the generator, which logs "You entered: Hello!".

In essence, this code demonstrates how generators can pause their execution, communicate with the caller, and resume their work.

2. Generator Delegation:

Generator Delegation: You can delegate one generator’s control to another generator using the yield* expression. This allows for modular and reusable generator functions.

function* generatorA() {
yield 1;
yield 2;
}

function* generatorB() {
yield* generatorA();
yield 3;
}

for (let value of generatorB()) {
console.log(value);
}
// Outputs: 1, 2, 3

  1. In the code, we have two generator functions, generatorA and generatorB. generatorA yields values 1 and 2, and generatorB uses the yield* syntax to delegate control to generatorA.
  2. This means that generatorB first yields the values from generatorA, which are 1 and 2, and then adds its own yield statement for 3.
  3. The for...of loop iterates through generatorB, printing each yielded value. As a result, the code outputs 1, 2, and 3.

This is showing how generators can be used together and combined to produce a sequence of values.

3. Infinite Sequences:

Generators are great for representing infinite sequences or streams of data. You can keep generating values indefinitely without consuming excessive memory.

function* infiniteCounter() {
let count = 0;
while (true) {
yield count++;
}
}

const counter = infiniteCounter();
console.log(counter.next().value); // 0
console.log(counter.next().value); // 1

  1. The code defines a generator function called infiniteCounter, which creates an infinite sequence of numbers starting from 0.
  2. Inside the generator, there's a while (true) loop that continually yields the current value of count and increments it.
  3. After creating an instance of the generator called counter, we use counter.next().value to retrieve the values from the generator.
  4. The first call returns 0, and the second call returns 1.

This demonstrates how generators can produce an endless sequence of values, allowing you to fetch them one at a time.

4. Async Operations:

You can use generators with async/await to simplify asynchronous code. The async keyword in front of a generator function allows you to use await inside the generator.

async function* asyncGenerator() {
let i = 0;
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate async operation
yield i++;
}
}

(async () => {
for await (let value of asyncGenerator()) {
console.log(value);
}
})();
// Outputs: 0 (after 1 second), 1 (after 2 seconds), 2 (after 3 seconds)

  1. The code defines an asynchronous generator function named asyncGenerator.
  2. This function yields values 0, 1, and 2 with one-second delays between each yield, simulating asynchronous operations.
  3. Using an immediately-invoked async function expression ((async () => { ... })()), it iterates through the values yielded by asyncGenerator.
  4. The for await (let value of asyncGenerator()) construct waits for each value to resolve asynchronously, ensuring that the values are logged in the specified order.
  5. As a result, the code outputs 0 after one second, 1 after two seconds, and 2 after three second.

This demonstrates how async generators can handle asynchronous tasks and provide values as they become available.

Generators are a versatile tool in JavaScript, and these hidden features showcase their ability to handle complex control flow and asynchronous operations elegantly.

Generators are a powerful and versatile feature of JavaScript that allows us to write more efficient and flexible code. They can be used to implement a wide range of use cases, including iterating over data, implementing asynchronous code, and creating infinite data streams.

Overall, generators are a valuable tool for any JavaScript developer. They can help us to write more efficient, flexible, and maintainable code.

If you are not already using generators in your JavaScript code, I encourage you to give them a try. They can be a powerful tool for writing more efficient, flexible, and maintainable code.

Stackademic

Thank you for reading until the end. Before you go:

  • Please consider clapping and following the writer! 👏
  • Follow us on Twitter(X), LinkedIn, and YouTube.
  • Visit Stackademic.com to find out more about how we are democratizing free programming education around the world.

Sources


Article information

Author: Ashley Clarke

Last Updated: 1699693922

Views: 786

Rating: 3.8 / 5 (70 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Ashley Clarke

Birthday: 2003-10-15

Address: 7711 Reese Coves Suite 915, West William, TN 17788

Phone: +4436954074497548

Job: Medical Doctor

Hobby: Bowling, Embroidery, Poker, Hiking, Stamp Collecting, Playing Guitar, Lock Picking

Introduction: My name is Ashley Clarke, I am a rich, Colorful, resolute, unwavering, candid, multicolored, unreserved person who loves writing and wants to share my knowledge and understanding with you.