JavaScript Generators
JavaScript generators are a powerful feature that allows you to handle lazy-loaded sequences of data and manage asynchronous operations more effectively. They offer an elegant way to pause and resume the execution of functions, making it easier to handle repetitive tasks, manage data pipelines, and implement complex algorithms.
A generator is a special type of function in JavaScript that can be paused and resumed. It allows you to generate a sequence of values over time, instead of computing them all at once. This is particularly useful for handling large datasets, working with async operations, or creating infinite sequences.
A generator function is defined with the function*
syntax (notice the asterisk *
), and it uses the yield
keyword to return values one at a time. Every time the generator function is called, it returns an iterator, which you can use to retrieve the values.
The key difference between regular functions and generator functions is that generators don't run to completion immediately. Instead, they pause when they reach the yield
keyword and resume when the iterator’s next()
method is called.
function* greet() {
yield "Hello";
yield "World";
yield "!";
}
const generator = greet();
console.log(generator.next()); // { value: 'Hello', done: false }
console.log(generator.next()); // { value: 'World', done: false }
console.log(generator.next()); // { value: '!', done: false }
console.log(generator.next()); // { value: undefined, done: true }
Here:
greet()
function is a generator function.next()
is called, it resumes execution until it reaches the next yield
statement.done: true
is returned.To define a generator function, use the function*
syntax.
function* myGenerator() {
// code
}
yield
Inside the generator function, use yield
to pause the execution and return a value.
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const gen = numbers();
console.log(gen.next()); // { value: 1, done: false }
next()
to Resume ExecutionTo resume the execution of the generator function and get the next yielded value, use the next()
method.
const generator = numbers();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }
You can also pass values into a generator function using next()
. This allows you to send data back to the generator, which can be useful for more complex workflows.
function* processData() {
const data1 = yield "Step 1";
console.log("Received:", data1);
const data2 = yield "Step 2";
console.log("Received:", data2);
}
const generator = processData();
console.log(generator.next()); // { value: 'Step 1', done: false }
console.log(generator.next("Data for Step 1")); // Received: Data for Step 1
// { value: 'Step 2', done: false }
console.log(generator.next("Data for Step 2")); // Received: Data for Step 2
// { value: undefined, done: true }
Here:
next("Data for Step 1")
, which is received by the generator's yield
statement.Generators are ideal for lazy evaluation, where values are generated on demand instead of being computed all at once. This is especially useful when dealing with large datasets or infinite sequences.
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const gen = fibonacci();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
In this example:
Generators are particularly useful for creating infinite sequences. They can run indefinitely, producing values one at a time when requested.
function* infiniteNumbers() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = infiniteNumbers();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
Here:
Generators can be useful in managing asynchronous operations, especially when combined with Promise
and async/await
. They allow you to pause execution at yield
and resume after a promise is resolved, which can simplify working with async code.
function* fetchData() {
const data1 = yield fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => res.json());
console.log("Data 1:", data1);
const data2 = yield fetch('https://jsonplaceholder.typicode.com/posts/2').then(res => res.json());
console.log("Data 2:", data2);
}
function run(generator) {
const iterator = generator();
function handle(result) {
if (result.done) return;
result.value.then(data => handle(iterator.next(data)));
}
handle(iterator.next());
}
run(fetchData);
Here:
yield
statement is executed.run()
function handles the asynchronous flow of the generator.Generators can maintain their internal state between yields. This is useful when you need to keep track of certain data while iterating through a sequence.
function* counter() {
let count = 0;
while (count < 3) {
yield count++;
}
}
const counterGen = counter();
console.log(counterGen.next().value); // 0
console.log(counterGen.next().value); // 1
console.log(counterGen.next().value); // 2
console.log(counterGen.next().value); // undefined
In this case:
count
variable that keeps track of the current state between yield
statements.return
and throw
in Generatorsreturn
statement to return a final value from a generator and exit it early.throw
to throw an error inside a generator.return
in Generators
function* simpleGenerator() {
yield 1;
yield 2;
return 3; // End the generator early and return this value
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: true }
throw
in Generators
function* throwExample() {
try {
yield 1;
throw new Error("Something went wrong");
} catch (e) {
console.log(e.message); // "Something went wrong"
}
}
const gen = throwExample();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.throw(new Error("Something went wrong"))); // Catch the error and log it