Skip to main content

JavaScript Generators & Iterators

JavaScript Iterators

Written by Published

An iterator is an object with a next method that hands out one value at a time.

Arrays, strings, Maps and Sets all work with for of because they follow this same protocol underneath.

Seeing it directly explains what a loop is really doing.

Example

Example

javascript

const numbers = [1, 2, 3];
const iterator = numbers[Symbol.iterator]();

console.log(iterator.next());
console.log(iterator.next());

Each call gives an object with a value and whether it is finished.

The Iterator Protocol

An iterator is any object with a next() method.

Each call returns { value, done }.

done becomes true once there is nothing left.

Syntax

Syntax

javascript

const iterator = { next() { /* ... */ } };
iterator.next(); // { value: ..., done: false }

No special syntax is needed; it is just an object shape.

Walking Through an Array Manually

for of does exactly this, one step per turn.

Example

Example

javascript

const colours = ["red", "green"];
const iterator = colours[Symbol.iterator]();

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

The third call has nothing left, so done is true.

Writing Your Own Iterator

Any object with a matching next method qualifies.

Example

Example

javascript

function countTo(max) {
  let current = 0;
  return {
    next() {
      current++;
      return current <= max
        ? { value: current, done: false }
        : { value: undefined, done: true };
    }
  };
}

const counter = countTo(3);

console.log(counter.next());
console.log(counter.next());

This behaves exactly like a built-in iterator.

Draining an Iterator by Hand

A loop that keeps calling next until done is what for of hides.

Example

Example

javascript

function countTo(max) {
  let current = 0;
  return {
    next() {
      current++;
      return current <= max
        ? { value: current, done: false }
        : { value: undefined, done: true };
    }
  };
}

const counter = countTo(3);
const values = [];
let result = counter.next();

while (!result.done) {
  values.push(result.value);
  result = counter.next();
}

console.log(values.join(","));

The output is 1,2,3.

Once Done, Always Done

A finished iterator keeps returning done: true.

Example

Example

javascript

const numbers = [1];
const iterator = numbers[Symbol.iterator]();

iterator.next();
console.log(iterator.next().done);
console.log(iterator.next().done);

Both later calls agree that it is finished.

Not Every Object Has One

A plain object does not have Symbol.iterator by default.

That is exactly why for of refuses to loop over one.

Example

Example

javascript

const plain = { a: 1, b: 2 };

console.log(typeof plain[Symbol.iterator]);
console.log(typeof [][Symbol.iterator]);

The output is undefined then function.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Iterators</title>
</head>
<body>

  <h1>Iterators</h1>

  <p id="out"></p>

  <script>
    const colours = ["red", "green", "blue"];
    const iterator = colours[Symbol.iterator]();

    const seen = [];
    let result = iterator.next();

    while (!result.done) {
      seen.push(result.value);
      result = iterator.next();
    }

    document.getElementById("out").textContent = seen.join(", ");
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try a string:

Call "hi"[Symbol.iterator]() and step through it the same way.

Important Points

  • An iterator is an object with a next() method.
  • Each call returns { value, done }.
  • for of is really just repeated calls to next.
  • A finished iterator keeps reporting done: true.
  • Plain objects do not have Symbol.iterator by default.

Conclusion

Every for of loop you have written was using this protocol.

Seeing it directly removes the mystery from generators, next.

It is also the foundation the next lesson builds custom iterables on.