Iterators provide a standard way to produce a sequence of values. Generators are functions that can pause and resume, making iterators easy to create.

Iterable Protocol

An object is iterable if it has a [Symbol.iterator]() method that returns an iterator. Arrays, strings, Maps, and Sets are iterable by default.

  let arr = [1, 2, 3];
let iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
  

for…of

Iterates over iterable values:

  for (let char of 'hello') {
    console.log(char); // h, e, l, l, o
}

for (let [key, value] of new Map([['a', 1], ['b', 2]])) {
    console.log(key, value);
}
  

Custom Iterable

  let range = {
    from: 1,
    to: 5,
    [Symbol.iterator]() {
        let current = this.from;
        let last = this.to;
        return {
            next() {
                if (current <= last) {
                    return { value: current++, done: false };
                }
                return { done: true };
            }
        };
    }
};

for (let n of range) {
    console.log(n); // 1, 2, 3, 4, 5
}
  

Generator Functions

Generators use function* and yield:

  function* countUp(max) {
    for (let i = 1; i <= max; i++) {
        yield i;
    }
}

let gen = countUp(3);
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 }

for (let n of countUp(5)) {
    console.log(n); // 1 through 5
}
  

yield* — Delegate to Another Generator

  function* inner() {
    yield 2;
    yield 3;
}

function* outer() {
    yield 1;
    yield* inner();
    yield 4;
}

[...outer()]; // [1, 2, 3, 4]
  

Passing Values to Generators

  function* gen() {
    let x = yield 1;
    console.log(x); // 'hello'
    yield 2;
}

let g = gen();
g.next();       // { value: 1, done: false }
g.next('hello'); // { value: 2, done: false }
  

Practical Use: Infinite Sequence

  function* idGenerator() {
    let id = 1;
    while (true) {
        yield id++;
    }
}

const ids = idGenerator();
console.log(ids.next().value); // 1
console.log(ids.next().value); // 2
  

Practical Use: Lazy Data Processing

  function* filter(iterable, predicate) {
    for (let item of iterable) {
        if (predicate(item)) yield item;
    }
}

let evens = filter([1, 2, 3, 4, 5], n => n % 2 === 0);
for (let n of evens) console.log(n); // 2, 4
  

Generators enable memory-efficient processing of large datasets by producing values on demand rather than building entire arrays in memory.