Skip to content
Plurafy

Iterators and Generators Deep Dive

Advanced iteration patterns

Create custom iterable objects, implement lazy evaluation, and handle infinite sequences.

Iterators and generators enable lazy evaluation and custom iteration patterns for memory-efficient code.

// Iterable: Has Symbol.iterator method
// Iterator: Has next() method returning {value, done}
const simpleIterator = {
[Symbol.iterator]() {
let count = 0;
return {
next: () => {
if (count < 3) {
return { value: count++, done: false };
}
return { done: true };
}
};
}
};
for (const value of simpleIterator) {
console.log(value); // 0, 1, 2
}
// Spread operator works too
const arr = [...simpleIterator]; // [0, 1, 2]
// Generator function (function*)
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
// Creates iterator
const gen = numberGenerator();
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 }
// Works in for...of
for (const value of numberGenerator()) {
console.log(value);
}
// Generate infinite sequence without memory overhead
function* infiniteSequence() {
let num = 0;
while (true) {
yield num++;
}
}
// Fibonacci with generators
function* fibonacci() {
let prev = 0, curr = 1;
while (true) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
// Take only what's needed
function* take(iterable, count) {
let taken = 0;
for (const item of iterable) {
if (taken++ < count) {
yield item;
} else {
break;
}
}
}
const first10Fibs = [...take(fibonacci(), 10)];
console.log(first10Fibs);
// [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// Delegate to another generator
function* delegator() {
yield* [1, 2, 3];
yield* generator();
}
function* generator() {
yield 4;
yield 5;
}
console.log([...delegator()]); // [1, 2, 3, 4, 5]
function* echoGenerator() {
const input1 = yield; // Receive value
console.log('Received:', input1);
const input2 = yield;
console.log('Received:', input2);
}
const gen = echoGenerator();
gen.next(); // Start generator
gen.next('first'); // Send 'first'
gen.next('second'); // Send 'second'
// Output:
// Received: first
// Received: second
function* errorGenerator() {
try {
yield 1;
yield 2;
} catch (error) {
console.log('Caught error:', error.message);
yield 3;
}
}
const gen = errorGenerator();
console.log(gen.next()); // { value: 1, done: false }
try {
gen.throw(new Error('Something went wrong'));
} catch (error) {
// Error propagates if not caught
}
console.log(gen.next()); // { value: 3, done: false }
// Read file line by line (streaming)
const fs = require('fs');
const readline = require('readline');
async function* readLines(filepath) {
const rl = readline.createInterface({
input: fs.createReadStream(filepath),
crlfDelay: Infinity
});
for await (const line of rl) {
yield line;
}
}
for await (const line of readLines('file.txt')) {
console.log(line);
}
// Chain operations
function* map(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
const result = [
...filter(
map([1, 2, 3, 4, 5], x => x * 2),
x => x > 5
)
];
console.log(result); // [6, 8, 10]