We use cookies to enhance your experience on the site
CodeWorlds

Iterators and Generators

In the Jurassic Park laboratory, scientists analyze dinosaur DNA sequences — element by element, never loading the entire genome into memory. Iterators and generators in JavaScript work similarly — they allow processing data element by element, lazily and efficiently.

The Iterable Protocol

Any object that implements the

[Symbol.iterator]()
method is iterable and can be used in a
for...of
loop.

1// Arrays, strings, Map, Set — are iterable by nature
2for (const char of 'ATCG') {
3  console.log(char); // A, T, C, G
4}
5
6for (const item of [1, 2, 3]) {
7  console.log(item); // 1, 2, 3
8}

Creating a Custom Iterator

1const dinoEnclosure = {
2  dinosaurs: ['Rex', 'Blue', 'Delta', 'Echo'],
3
4  [Symbol.iterator]() {
5    let index = 0;
6    const dinos = this.dinosaurs;
7
8    return {
9      next() {
10        if (index < dinos.length) {
11          return { value: dinos[index++], done: false };
12        }
13        return { value: undefined, done: true };
14      }
15    };
16  }
17};
18
19// Now we can use for...of!
20for (const dino of dinoEnclosure) {
21  console.log('Dinosaur:', dino);
22}
23
24// And the spread operator
25const allDinos = [...dinoEnclosure];
26console.log(allDinos); // ['Rex', 'Blue', 'Delta', 'Echo']

for...of vs for...in

1const park = { name: 'Jurassic', size: 'large', open: false };
2const species = ['T-Rex', 'Velociraptor', 'Triceratops'];
3
4// for...in — iterates over KEYS (of objects and arrays)
5for (const key in park) {
6  console.log(key); // 'name', 'size', 'open'
7}
8
9for (const index in species) {
10  console.log(index); // '0', '1', '2' (strings!)
11}
12
13// for...of — iterates over VALUES (of iterables)
14for (const animal of species) {
15  console.log(animal); // 'T-Rex', 'Velociraptor', 'Triceratops'
16}
17
18// for...of does NOT work on plain objects!
19// for (const val of park) {} // TypeError!
20// But works on Object.entries():
21for (const [key, val] of Object.entries(park)) {
22  console.log(key, val);
23}

Generators (function*)

Generators are special functions that can pause their execution and resume it later.

1function* dinoGenerator() {
2  console.log('Generating first dinosaur...');
3  yield 'T-Rex';
4
5  console.log('Generating second...');
6  yield 'Velociraptor';
7
8  console.log('Generating third...');
9  yield 'Triceratops';
10
11  console.log('Generation complete!');
12}
13
14const gen = dinoGenerator();
15
16console.log(gen.next()); // { value: 'T-Rex', done: false }
17console.log(gen.next()); // { value: 'Velociraptor', done: false }
18console.log(gen.next()); // { value: 'Triceratops', done: false }
19console.log(gen.next()); // { value: undefined, done: true }

Infinite Generator

1function* idGenerator(prefix) {
2  let id = 1;
3  while (true) {
4    yield prefix + '-' + String(id).padStart(3, '0');
5    id++;
6  }
7}
8
9const dinoIds = idGenerator('DINO');
10console.log(dinoIds.next().value); // 'DINO-001'
11console.log(dinoIds.next().value); // 'DINO-002'
12console.log(dinoIds.next().value); // 'DINO-003'
13// Never ends — generates on demand!

yield* — Delegation to Another Generator

1function* carnivores() {
2  yield 'T-Rex';
3  yield 'Velociraptor';
4}
5
6function* herbivores() {
7  yield 'Triceratops';
8  yield 'Brachiosaurus';
9}
10
11function* allDinosaurs() {
12  yield* carnivores();
13  yield* herbivores();
14}
15
16for (const dino of allDinosaurs()) {
17  console.log(dino);
18}
19// T-Rex, Velociraptor, Triceratops, Brachiosaurus

Async Generators

1async function* fetchDinoPages(apiUrl) {
2  let page = 1;
3  let hasMore = true;
4
5  while (hasMore) {
6    const response = await fetch(apiUrl + '?page=' + page);
7    const data = await response.json();
8
9    yield data.results;
10
11    hasMore = data.hasNextPage;
12    page++;
13  }
14}
15
16// Usage with for await...of
17async function loadAllDinos() {
18  for await (const batch of fetchDinoPages('/api/dinosaurs')) {
19    console.log('Loaded batch:', batch.length, 'dinosaurs');
20  }
21}

Practical Applications

1// 1. Lazy evaluation — process on demand
2function* fibonacci() {
3  let a = 0, b = 1;
4  while (true) {
5    yield a;
6    [a, b] = [b, a + b];
7  }
8}
9
10// Take only first 10
11const fib = fibonacci();
12const first10 = [];
13for (let i = 0; i < 10; i++) {
14  first10.push(fib.next().value);
15}
16console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
17
18// 2. Pagination — load pages on demand
19function* paginate(items, pageSize) {
20  for (let i = 0; i < items.length; i += pageSize) {
21    yield items.slice(i, i + pageSize);
22  }
23}
24
25const allSpecies = ['A','B','C','D','E','F','G','H'];
26const pages = paginate(allSpecies, 3);
27console.log(pages.next().value); // ['A', 'B', 'C']
28console.log(pages.next().value); // ['D', 'E', 'F']
29console.log(pages.next().value); // ['G', 'H']

"Iterators and generators are like an assembly line in the laboratory" — says the scientist. "You do not have to process everything at once — you get elements exactly when you need them!"

Go to CodeWorlds