JavaScript Generators Tutorial - Section 3: Advanced Patterns
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!
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
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]
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]
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]
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]
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]
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]
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]
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']
function* repeat(value) {
while (true) {
yield value;
}
}
const zeros = repeat(0);
console.log([...take(zeros, 5)]); // [0, 0, 0, 0, 0]
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]
take() to limit infinite sequencesfilter(), map() for pipelinesNow let's explore async generators for handling asynchronous data streams!