JavaScript > ES6 and Beyond > Iterators and Generators > Generator functions
Simple Number Generator
This snippet demonstrates a basic generator function that yields a sequence of numbers. It illustrates the fundamental syntax and behavior of generators in JavaScript.
Code Example
This code defines a generator function called `numberGenerator`. The `function*` syntax signifies that this is a generator function. Inside the function, a `while` loop iterates from 1 to `max`. The `yield` keyword is crucial. It pauses the function's execution and returns the value of `i` to the caller. The next time `generator.next()` is called, the function resumes execution from where it left off, incrementing `i` and yielding the next value. When `i` exceeds `max`, the loop terminates, and subsequent calls to `generator.next()` return an object with `value: undefined` and `done: true` indicating that the generator is exhausted.
function* numberGenerator(max) {
let i = 1;
while (i <= max) {
yield i++;
}
}
const generator = numberGenerator(5);
console.log(generator.next().value); // Output: 1
console.log(generator.next().value); // Output: 2
console.log(generator.next().value); // Output: 3
console.log(generator.next().value); // Output: 4
console.log(generator.next().value); // Output: 5
console.log(generator.next().value); // Output: undefined
Concepts Behind the Snippet
Generator functions are a special type of function that can be paused and resumed. Unlike regular functions that execute to completion and return a single value, generator functions can yield multiple values over time. The `yield` keyword is the heart of a generator. Each time `yield` is encountered, the generator's state is saved, and the yielded value is returned to the caller. When the caller requests the next value (using `next()`), the generator resumes from where it left off. This allows for lazy evaluation and efficient handling of potentially large or infinite sequences of data.
Real-Life Use Case Section
Imagine processing a very large log file. Reading the entire file into memory could be inefficient or even impossible. A generator function can read the file line by line, yielding each line as needed. This avoids loading the entire file into memory at once, significantly improving performance and memory usage. Another common use case is generating infinite sequences, such as Fibonacci numbers or prime numbers, without storing them all in memory. Generators are also useful in asynchronous programming with `async/await` to handle streams of data.
Best Practices
Keep generator functions focused on generating a specific sequence of values. Avoid performing complex operations within the generator function itself; delegate those tasks to other functions or modules. Handle errors gracefully within the generator to prevent unexpected crashes. Ensure that the generator eventually terminates to avoid infinite loops and potential memory leaks.
Interview Tip
Be prepared to explain the difference between a regular function and a generator function. Understand the purpose of the `yield` keyword and how it affects the function's execution. Be able to demonstrate how to create and use a generator function, including how to iterate over its yielded values using `next()` or a `for...of` loop.
When to use them
Use generator functions when you need to generate a sequence of values on demand, especially when dealing with large or infinite datasets. They are also valuable for implementing iterators and simplifying asynchronous code.
Memory footprint
Generator functions are memory-efficient because they only generate values as needed. They don't store the entire sequence in memory at once, reducing memory consumption, especially when working with large datasets.
Alternatives
Alternatives to generator functions include using arrays to store sequences of values or using traditional iterator objects. However, arrays may require more memory, and iterator objects can be more complex to implement manually.
Pros
Generator functions offer lazy evaluation, memory efficiency, and simplified asynchronous programming. They can also improve code readability and maintainability.
Cons
Generator functions can add complexity to your code if not used carefully. They can also be less performant than traditional functions for small datasets where the overhead of pausing and resuming execution outweighs the benefits of lazy evaluation.
FAQ
-
What is the difference between `function` and `function*`?
The `function` keyword defines a regular function, while `function*` defines a generator function. Generator functions can be paused and resumed using the `yield` keyword, while regular functions execute to completion and return a single value. -
How do I iterate over the values yielded by a generator?
You can use the `next()` method to retrieve the next value in the sequence. Alternatively, you can use a `for...of` loop to iterate over all the yielded values. -
What happens when a generator reaches the end of its sequence?
When a generator reaches the end of its sequence, the `next()` method returns an object with `value: undefined` and `done: true`.