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:
function*
is used to declare a generator function.- Inside the generator function, we initialize two variables
a
andb
to the first two Fibonacci numbers (0 and 1). - We enter an infinite loop using
while (true)
to keep generating Fibonacci numbers. - The
yield
keyword is used to yield (return) the current value ofa
as part of the sequence. - We then update
a
andb
to the next Fibonacci numbers using destructuring assignment. - The
for
loop generates and prints the first 10 Fibonacci numbers by callingfibonacciSequence.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!"
- The code defines a generator function called
twoWayGenerator
. This generator yields the message "Please provide a value:" and pauses, waiting for input. - When we create an instance of this generator and call
gen.next().value
, it starts the generator and logs the message. - 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
- In the code, we have two generator functions,
generatorA
andgeneratorB
.generatorA
yields values1
and2
, andgeneratorB
uses theyield*
syntax to delegate control togeneratorA
. - This means that
generatorB
first yields the values fromgeneratorA
, which are1
and2
, and then adds its own yield statement for3
. - The
for...of
loop iterates throughgeneratorB
, printing each yielded value. As a result, the code outputs1
,2
, and3
.
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
- The code defines a generator function called
infiniteCounter
, which creates an infinite sequence of numbers starting from 0. - Inside the generator, there's a
while (true)
loop that continually yields the current value ofcount
and increments it. - After creating an instance of the generator called
counter
, we usecounter.next().value
to retrieve the values from the generator. - The first call returns
0
, and the second call returns1
.
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)
- The code defines an asynchronous generator function named
asyncGenerator
. - This function yields values
0
,1
, and2
with one-second delays between each yield, simulating asynchronous operations. - Using an immediately-invoked async function expression (
(async () => { ... })()
), it iterates through the values yielded byasyncGenerator
. - 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. - As a result, the code outputs
0
after one second,1
after two seconds, and2
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.
Author: Ashley Clarke
Last Updated: 1699693922
Views: 1135
Rating: 3.8 / 5 (70 voted)
Reviews: 83% of readers found this page helpful
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.