JavaScript iterator and generator: iterators, js iterator, and generator patterns

javascript iterator, js iterator, iterator javascript, iterator js, javascript iterators, iterators javascript, iterators js, js iterators, iterators and generators in javascript, generator pattern javascript: Iterator protocol, function*, yield, for of, return value.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

JavaScript iterator and generator: iterators, js iterator, and generator patterns

JavaScript iterators and generators implement the iterator protocol: an iterator exposes next() returning { value, done }, while an iterable exposes Symbol.iterator so for...of and spread can pull a fresh iterator. A generator (function*) pauses with yield and resumes on the next next() call—handy for lazy sequences and readable control flow, and it pairs naturally with async patterns you may already use via async/await. This article walks through hand-rolled iterators, generator syntax, values passed into next, bounded demos of “infinite” generators, return semantics with spread/for...of, and a small for...of loop over a generator.

Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.


Manual iterator (javascript iterator without function*)

Diagram: IteratorResult object showing value and done fields returned from each next call

javascript
const numbers = [1, 2, 3, 4];
const numbersIterator = {
  index: 0,
  next() {
    if (this.index < numbers.length) {
      return { value: numbers[this.index++], done: false };
    }
    return { done: true };
  },
};

let result = numbersIterator.next();
while (!result.done) {
  console.log(result.value);
  result = numbersIterator.next();
}
Output

You should see 4 lines, in order: 1, 2, 3, 4.


Generator that yields values (function* / yield)

Diagram: generator execution pauses at each yield until the iterator is resumed with next

javascript
function* numbersGenerator() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

const numbers = numbersGenerator();
console.log(numbers.next().value);
console.log(numbers.next().value);
console.log(numbers.next().value);
Output

You should see 3 lines, in order: 1, 2, 3.


Generator pattern: send a value into next

The first next() must usually run up to the first yield before you can inject a value.

javascript
function* addGenerator(x) {
  const y = yield;
  return x + y;
}

const add = addGenerator(5);
const r1 = add.next();
const r2 = add.next(7);
console.log(r1.value, r1.done);
console.log(r2.value, r2.done);
Output

You should see 2 lines, in order: undefined false, 12 true.


“Infinite” generator (bounded demo)

javascript
function* countGenerator() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const count = countGenerator();
console.log(count.next().value);
console.log(count.next().value);
console.log(count.next().value);
Output

You should see 3 lines, in order: 0, 1, 2.


Generator over an array (yield in a loop)

javascript
function* arrayIterator(array) {
  for (const value of array) {
    yield value;
  }
}

const iterator = arrayIterator([1, 2, 3]);
console.log(iterator.next().value);
console.log(iterator.next().value);
console.log(iterator.next().value);
Output

You should see 3 lines, in order: 1, 2, 3.


return from a generator vs spread / for...of

for...of and [...generator()] consume yielded values but discard the object returned when done is true after a return statement. Manual next() calls still see that { value, done: true }.

javascript
function* oneAndDone() {
  yield 1;
  return "done";
}

console.log(JSON.stringify([...oneAndDone()]));
const generator = oneAndDone();
const a = generator.next();
console.log(a.value, a.done);
const b = generator.next();
console.log(b.value, b.done);
const c = generator.next();
console.log(c.value, c.done);
Output

You should see 4 lines, in order: [1], 1 false, done true, undefined true.


for...of over a generator

Any object obeying the iterator protocol works with for...of when it is iterable (generators return iterable iterators).

javascript
function* triple() {
  yield "a";
  yield "b";
  yield "c";
}

const out = [];
for (const v of triple()) {
  out.push(v);
}
console.log(out.join(","));
Output

You should see one line logging a,b,c.


Async code and async generators

Older patterns sometimes yield Promises from normal generators and resume with next(resolvedValue); today, prefer async function* with for await...of when you iterate async sources. Those APIs are intentionally not exercised here so the synchronous examples stay deterministic.


Summary

Javascript iterator and generator topics cover two related protocols: synchronous iterators (Symbol.iterator with next() returning { value, done }) and function* generators that yield values lazily. People ask when generators beat arrays—when streams are large, expensive to compute, or should pause between steps—and when async generators matter for pulling async I/O without buffering everything.

Another FAQ is how for...of interacts with iterators: any object exposing Symbol.iterator works, including generator objects, which is why generator bodies read like straight-line code even though execution hops between yields. When async sources appear, reach for async function* plus for await...of instead of trying to manually drive next on Promises.


References

Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs …