Lesson 21 of 80
The iterator protocol is the contract that makes `for...of` and spread work on anything; generators are a shortcut for writing objects that follow it.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
Does calling `countTo(3)` (a generator function) run the function body immediately?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
The iterator protocol is a simple contract: an object is iterable if it has a [Symbol.iterator] method that returns an object with a .next() method, and .next() returns { value, done }. Anything following that contract works with for...of, spread, and destructuring — arrays and strings follow it natively; you can make your own objects follow it too.
Writing that protocol by hand is tedious. A generator function (function*) writes it for you: calling it doesn't run the function, it returns an iterator, and every yield pauses execution and produces one value.
function* countTo(n) {
for (let i = 1; i <= n; i++) {
yield i; // pause here, hand back i
}
}
for (const num of countTo(3)) console.log(num); // 1, 2, 3
const it = countTo(2);
it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
it.next(); // { value: undefined, done: true }
The function's execution genuinely pauses at each yield and resumes exactly where it left off on the next .next() call — local variables and all. That's different from a normal function, which runs start-to-finish in one go.
function* countTo(3) — calling it does NOT run the body. It returns an iterator, paused at the very start.
function* doesn't run the function body on call — it returns an iterator object immediately; the body only executes as .next() is called, step by step.yield can receive a value too: whatever you pass into .next(value) becomes the result of the previous yield expression inside the generator — generators can be a two-way channel, not just an output stream.[Symbol.iterator]() method — the easiest way to implement one is to make it a generator method, since function* already returns a valid iterator."Make this custom object work with for...of" tests whether you understand the iterator protocol as a real contract rather than syntax that only applies to arrays — and generators are the practical tool for implementing it without writing .next() by hand.