Definition
A generator is a function that:
- can stop mid-execution
- can resume where it left off
- produces values one at a time
- It is defined with
function*
Example
function* numbers() {
yield 1;
yield 2;
yield 3;
}
const gen = numbers();
console.log(gen.next());
// { value: 1, done: false }
console.log(gen.next());
// { value: 2, done: false }
console.log(gen.next());
// { value: 3, done: false }
console.log(gen.next());
// { value: undefined, done: true }What happen here?
- every
yield: returns a value, stops the execution and saves the internal state. What is actually returned is an object that wraps the actual value providing information about the current statedone: [boolean]
A generator is a "stoppable" function that is extremely helpful when we have to iterate through large objects, list of data, or generate next values based on our custom logic.
Use cases
Generating sequences
Generating infinite sequences or counters without creating large arrays. In this case we’ll have a while(true), meaning that the counter can potentially go on forever.
function* counter() {
let i = 0;
while (true) {
yield i++;
}
}
const c = counter();
console.log(c.next().value); // 0
console.log(c.next().value); // 1
console.log(c.next().value); // 2This is particularly handy given that we have never defined the “next” value, it is created “on the fly” when is needed.
Reading files Instead of giving back the whole file, we can use a generator to process one line at the time:
function* readLines(text) {
const lines = text.split("\n");
for (const line of lines) {
yield line;
}
}Modern usage
Generators are iterable. The following code implicitly calls .next() at every iteration:
for (const c of counter()) {
console.log(c);
}We can of course deal with async operations:
async function* delayedNumbers() {
yield 1;
await new Promise(r => setTimeout(r, 1000));
yield 2;
await new Promise(r => setTimeout(r, 1000));
yield 3;
}