Learning Objectives

  • Create infinite sequences safely
  • Master lazy evaluation patterns
  • Build utility functions for infinite generators
  • Understand memory efficiency

Infinite Generators

Generators can represent infinite sequences without consuming infinite memory.

function* infiniteNumbers() {
  let n = 0;
  while (true) {
    yield n++;
  }
}

const numbers = infiniteNumbers();
console.log(numbers.next().value); // 0
console.log(numbers.next().value); // 1
console.log(numbers.next().value); // 2
// Can continue forever!

Classic Infinite Sequences

Fibonacci Sequence

function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5

Prime Numbers

function* primes() {
  yield 2;
  const found = [2];
  let candidate = 3;
  
  while (true) {
    let isPrime = true;
    
    for (const prime of found) {
      if (prime * prime > candidate) break;
      if (candidate % prime === 0) {
        isPrime = false;
        break;
      }
    }
    
    if (isPrime) {
      yield candidate;
      found.push(candidate);
    }
    
    candidate += 2;
  }
}

const primeGen = primes();
console.log([...take(primeGen, 10)]);
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Utility Functions

Take - Limit Values

function* take(iterable, n) {
  let count = 0;
  for (const value of iterable) {
    if (count++ >= n) break;
    yield value;
  }
}

const nums = infiniteNumbers();
console.log([...take(nums, 5)]); // [0, 1, 2, 3, 4]

Skip - Skip Values

function* skip(iterable, n) {
  let count = 0;
  for (const value of iterable) {
    if (count++ < n) continue;
    yield value;
  }
}

const nums = infiniteNumbers();
console.log([...take(skip(nums, 5), 3)]); // [5, 6, 7]

Filter - Conditional Values

function* filter(iterable, predicate) {
  for (const value of iterable) {
    if (predicate(value)) {
      yield value;
    }
  }
}

const nums = infiniteNumbers();
const evens = filter(nums, n => n % 2 === 0);
console.log([...take(evens, 5)]); // [0, 2, 4, 6, 8]

Map - Transform Values

function* map(iterable, transform) {
  for (const value of iterable) {
    yield transform(value);
  }
}

const nums = infiniteNumbers();
const squared = map(nums, n => n * n);
console.log([...take(squared, 5)]); // [0, 1, 4, 9, 16]

Combining Utilities

const nums = infiniteNumbers();
const result = take(
  map(
    filter(nums, n => n % 2 === 0),
    n => n * n
  ),
  5
);

console.log([...result]); // [0, 4, 16, 36, 64]

Real-World: Random Data Generator

function* randomNumbers(min = 0, max = 100) {
  while (true) {
    yield Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

function* randomDates(startYear = 2020, endYear = 2025) {
  while (true) {
    const year = Math.floor(Math.random() * (endYear - startYear + 1)) + startYear;
    const month = Math.floor(Math.random() * 12);
    const day = Math.floor(Math.random() * 28) + 1;
    yield new Date(year, month, day);
  }
}

const randomNums = randomNumbers(1, 10);
console.log([...take(randomNums, 5)]); // [7, 3, 9, 1, 5] (random)

const randomDts = randomDates(2020, 2025);
console.log([...take(randomDts, 3)]); // [Date, Date, Date]

Cycle Pattern

function* cycle(array) {
  while (true) {
    for (const item of array) {
      yield item;
    }
  }
}

const colors = cycle(['red', 'green', 'blue']);
console.log([...take(colors, 7)]);
// ['red', 'green', 'blue', 'red', 'green', 'blue', 'red']

Repeat Pattern

function* repeat(value) {
  while (true) {
    yield value;
  }
}

const zeros = repeat(0);
console.log([...take(zeros, 5)]); // [0, 0, 0, 0, 0]

Range with Step

function* range(start = 0, end = Infinity, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

console.log([...take(range(0, Infinity, 5), 5)]);
// [0, 5, 10, 15, 20]

Key Takeaways

  • ✅ Infinite generators use constant memory
  • ✅ Values computed on demand (lazy)
  • ✅ Use take() to limit infinite sequences
  • ✅ Combine with filter(), map() for pipelines
  • ✅ Perfect for mathematical sequences
  • ✅ Great for random data generation

Next Steps

Now let's explore async generators for handling asynchronous data streams!