Iterators and Generators Deep Dive
Advanced iteration patterns
Challenge
Section titled “Challenge”Create custom iterable objects, implement lazy evaluation, and handle infinite sequences.
Explanation
Section titled “Explanation”Iterators and generators enable lazy evaluation and custom iteration patterns for memory-efficient code.
Iterator Protocol
Section titled “Iterator Protocol”// 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 tooconst arr = [...simpleIterator]; // [0, 1, 2]Generator Functions
Section titled “Generator Functions”// Generator function (function*)function* numberGenerator() { yield 1; yield 2; yield 3;}
// Creates iteratorconst 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...offor (const value of numberGenerator()) { console.log(value);}Lazy Evaluation
Section titled “Lazy Evaluation”// Generate infinite sequence without memory overheadfunction* infiniteSequence() { let num = 0; while (true) { yield num++; }}
// Fibonacci with generatorsfunction* fibonacci() { let prev = 0, curr = 1; while (true) { yield curr; [prev, curr] = [curr, prev + curr]; }}
// Take only what's neededfunction* 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]Generator Delegation
Section titled “Generator Delegation”// Delegate to another generatorfunction* delegator() { yield* [1, 2, 3]; yield* generator();}
function* generator() { yield 4; yield 5;}
console.log([...delegator()]); // [1, 2, 3, 4, 5]Send Values to Generator
Section titled “Send Values to Generator”function* echoGenerator() { const input1 = yield; // Receive value console.log('Received:', input1);
const input2 = yield; console.log('Received:', input2);}
const gen = echoGenerator();gen.next(); // Start generatorgen.next('first'); // Send 'first'gen.next('second'); // Send 'second'
// Output:// Received: first// Received: secondGenerator Error Handling
Section titled “Generator Error Handling”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 }Practical Examples
Section titled “Practical Examples”// 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 operationsfunction* 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]