An iterable supplies a method at Symbol.iterator. Calling that method produces an iterator with next(), whose result is an object containing value and done. for/of uses this protocol for you. An array is iterable; the iterator created from it holds a current position.
Symbols are unique property keys. Symbol.iterator is a shared well-known symbol that JavaScript recognizes as a protocol hook. It does not mean an ordinary property named “iterator”. You usually consume existing iterables or write generators rather than implementing every protocol manually.
Two iterators from one array progress independently. One iterator can be exhausted and then keeps reporting done. An iterable is not necessarily reusable: a generator object is itself a stateful iterator, so iterating it again does not restart it.
The task pool will accept a synchronous iterable and pull values only when a worker slot is available. This makes demand visible, but returning every result still requires memory proportional to the number of results.
Predict, run, and explain
Write the output and trace the important state changes before checking it. Then run the program. Each editor is independent. Async examples end with main() so this editor waits for their work.
const values = [10, 20];
const first = values[Symbol.iterator]();
const second = values[Symbol.iterator]();
console.log(first.next().value);
console.log(first.next().value);
console.log(second.next().value);
console.log(first.next().done);Check your prediction
10
20
10
true One iterable can produce independent cursors
Iterable and iterator are roles, not interchangeable names for a collection. The iterable can create an iterator; the iterator tracks progress and returns result records. A next result describes both whether a value was produced and whether iteration has finished.
An iterator may be the iterable itself, as with a generator object, so a second loop can find it already exhausted. Reuse requires creating a fresh iterator or calling the generator function again, not merely putting the same stateful object in another for-of loop.
const values = ["a", "b"];
const cursor = values[Symbol.iterator]();
console.log(cursor.next().done);
console.log(cursor.next().value);
console.log(cursor.next().done);
console.log(cursor.next().done);Check your prediction
false
b
true
true The first call consumed a even though it only printed done. Reading one field of the result does not undo the advancement.
const values = ["a", "b"];
const a = values[Symbol.iterator]();
const b = values[Symbol.iterator]();
a.next();
console.log(a.next().value);
console.log(b.next().value);Check your prediction
b
a Each iterator owns its position. The array data is shared, but the cursors are not one shared progress counter.
Try the rule on new inputs
Collect every next result for an empty and a one-item array. Mark which call consumes a value. Then explain why collecting all results from an infinite iterable cannot finish.
Your turn
Iterable Protocols: practise
Create an iterator over [“a”, “b”]. Read both values and then print true for its exhausted done flag. Create a fresh iterator and show that it starts at a.
// Call Symbol.iterator to create a fresh cursor each time.Hint 1: the idea
The iterable and the iterator are separate roles.
Hint 2: a first step
Store the first iterator before calling next repeatedly.
Show one solution
const values = ["a", "b"];
const iterator = values[Symbol.iterator]();
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().done);
console.log(values[Symbol.iterator]().next().value);a
b
true
aExplain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?
Change it and test it
Inspect Object.keys(values) and explain why it does not show Symbol.iterator. Test an empty list. Then write down why an infinite iterable would be unsuitable for a helper that collects all results before returning.
Completion is saved in this browser only. It does not measure mastery.