Iteratoren und Generatoren in JavaScript (Node.js): Eine ausführliche Anleitung
What is it and why? (a real-life analogy)
Imagine you are a librarian, and in front of you is a huge shelf with thousands of books. You need to write down the titles of all books that start with the letter 'P'. How would you do it?
The first way (bad): You take all the books from the shelf, pile them into a huge heap on the floor, and only then start sorting through them. Problem: you'll run out of space, you'll get tired of carrying heavy loads, and if there are a million books, you simply won't be able to hold them all in your head or in the room.
The second way (good): You approach the shelf, take the first book, look at the title, write it down if necessary, and put it back. Then you take the second, the third... You don't store all the books with you — you simply "go through" them one by one, performing the necessary action on each.
The same thing happens in programming. We have data: arrays, strings, lists, results of database queries. We often need to go through all the elements in order and do something with them. But storing all the data in memory at once is expensive and often impossible.
An Iterator is precisely that "librarian" who knows how to walk along the shelf and hand you books one by one. It knows where the beginning is, where the end is, and how to move to the next element.
A Generator is a "factory" for producing iterators. You tell the generator: "I need numbers from 1 to infinity, but only even ones." And the generator creates an iterator that will give you these numbers one at a time, without trying to create an infinite list in memory.
💡 Important: Iterators and generators are not about what data you have, but about how you access it. They are tools for efficient traversal.
Why is this needed in real life?
- Memory efficiency: Imagine you are reading a file of 10 gigabytes. If you load it entirely into memory, the program will crash. An iterator allows you to read one line at a time.
- Working with infinite sequences: For example, an infinite data stream from a temperature sensor. You cannot create an array of "all future temperatures".
- Lazy evaluation: A value is computed only when it is actually needed. This speeds up programs.
- Code readability: A
for...ofloop with an iterator looks clearer than manual index management.
Theory: how it works internally
The Iterator Protocol
In JavaScript, there is a strict "contract" — a protocol that any object must follow to be iterable. This contract consists of two parts:
1. The object must have a next() method
This method is called each time we want to get the next element. It returns an object with two fields:
value— the value of the current element (can be of any type).done— a boolean value:falseif there are more elements, andtrueif the iteration is finished.
2. The object must be "iterable"
For an object to be used in a for...of loop or with the spread operator ([...obj]), it must have a Symbol.iterator method. This method returns the iterator itself (the object with the next method).
🔍 Detail: Symbol.iterator is a special symbol that cannot accidentally coincide with the name of a regular property. It's like a secret password that tells JavaScript: "This object knows how to be iterated."
What it looks like "under the hood"
When you write:
for (let item of myArray) {
console.log(item);
}
JavaScript does approximately the following:
// 1. Gets the iterator from the array
let iterator = myArray[Symbol.iterator]();
// 2. Starts an infinite loop
let result = iterator.next();
while (!result.done) {
let item = result.value; // current element
console.log(item); // body of your loop
result = iterator.next(); // move to the next one
}
That is, the for...of loop is just "syntactic sugar" for manually calling next().
Generators: iterator factories
A generator is a function that returns an iterator. But not a simple one — a "lazy" one. A regular function executes from start to finish and returns a single value. A generator can "freeze" its execution at any point, return an intermediate value, and then continue from the same place.
A generator is denoted by an asterisk after function:
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
The keyword yield is like return, but with the ability to "come back". When the generator reaches yield, it returns the value and stops. On the next call to next(), it continues execution from the next line.
⚡ Analogy: Imagine you are reading a book and placing a bookmark on each interesting page. yield is that bookmark. You read a page, put a bookmark in, close the book. Then you open it — and continue reading from the same place.
Internal structure of a generator
When you call a generator function, it does not execute its code immediately. Instead, it returns an iterator object (Generator Object). This object has:
- The
next()method — starts execution until the nextyield. - The
return()method — terminates the generator early. - The