J

JavaScript Handbook

Clean • Professional

Generators in JavaScript

2 minute

Generators in JavaScript

A generator is a special type of function that can pause and resume its execution.

  • Declared using the function* syntax.
  • Uses yield to return values one at a time.
  • Automatically produces an iterator when called.

Syntax :

function* generatorFunction() {
  yield value1;
  yield value2;
  return finalValue; // optional
}
  • yield: Pauses the generator and returns a value.
  • return: Ends the generator; value is returned and done becomes true.

Basic 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 }

Characteristics of Generators

  1. Iterator Protocol: Generators implement the iterator protocol automatically.
  2. Lazy Evaluation: Values are generated on-demand, saving memory for large sequences.
  3. Stateful: Generators maintain their execution context between yield calls.
  4. Single-use: Once finished, a generator cannot be restarted; you need a new generator instance.

Iterating Generators

You can iterate over a generator using:

a. for...of Loop

function* letters() {
  yield 'A';
  yield 'B';
  yield 'C';
}

for (const letter of letters()) {
  console.log(letter);
}
// Output: A B C

b. while Loop with .next()

const gen = letters();
let result = gen.next();
while (!result.done) {
  console.log(result.value);
  result = gen.next();
}
// Output: A B C

Passing Values to Generators

You can pass values back into a generator using .next(value):

function* counter() {
  const start = yield "Enter start value:";
  yield start + 1;
  yield start + 2;
}

const gen = counter();
console.log(gen.next());       // { value: "Enter start value:", done: false }
console.log(gen.next(5));      // { value: 6, done: false }
console.log(gen.next());       // { value: 7, done: false }

Error Handling in Generators

Generators can handle errors using .throw():

function* genFunc() {
  try {
    yield 1;
  } catch (err) {
    console.log('Caught:', err);
  }
}

const gen = genFunc();
console.log(gen.next());       // { value: 1, done: false }
gen.throw(new Error("Oops"));  // Caught: Error: Oops

Practical Example: Infinite Sequence

Generators are perfect for lazy sequences:

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

Use Cases

  1. Lazy Evaluation: Generate large or infinite sequences without memory overhead.
  2. Custom Iteration: Simplify iteration logic for complex data structures.
  3. Asynchronous Programming: Combine with async and await for advanced flow control.
  4. State Machines: Maintain execution context across multiple steps.

Article 0 of 0