stackademic

The leading education platform for anyone with an interest in software development.

JavaScript Generators and Iterators

Produce values lazily with iterators and pausable generator functions

Overview

An iterator is any object with a next() method that returns { value, done }, and it becomes iterable by implementing Symbol.iterator. Generators (function*) are a concise way to build iterators, pausing at each yield and resuming on the next call. Together they enable lazy, on-demand sequences that can even be infinite.

Syntax / Usage

Calling a generator returns an iterator without running the body. Each next() advances to the following yield; yield* delegates to another iterable.

function* range(start, end, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i
  }
}

const it = range(0, 5)
console.log(it.next()) // { value: 0, done: false }
console.log([...range(0, 5)]) // [0, 1, 2, 3, 4]

function* combined() {
  yield* range(0, 2)
  yield* ['a', 'b']
}
console.log([...combined()]) // [0, 1, 'a', 'b']

Examples

Implement a custom iterable by defining Symbol.iterator:

const evens = {
  *[Symbol.iterator]() {
    let n = 0
    while (true) yield (n += 2) // infinite sequence
  },
}

const iter = evens[Symbol.iterator]()
console.log(iter.next().value) // 2
console.log(iter.next().value) // 4

Generators can receive values back through next(value):

function* accumulator() {
  let total = 0
  while (true) {
    const amount = yield total // pauses, resumes with the passed value
    total += amount
  }
}

const acc = accumulator()
acc.next()       // prime the generator
console.log(acc.next(10).value) // 10
console.log(acc.next(5).value)  // 15

Take only the first N items from an infinite generator:

function take(iterable, count) {
  const out = []
  for (const value of iterable) {
    if (out.length >= count) break
    out.push(value)
  }
  return out
}

function* naturals() {
  let n = 1
  while (true) yield n++
}

console.log(take(naturals(), 3)) // [1, 2, 3]

Common Mistakes

  • Spreading an infinite generator with [...gen], which never terminates
  • Forgetting the first next() call is needed to reach the first yield when passing values in
  • Assuming a generator can be restarted—once exhausted, it stays done
  • Confusing yield with return; a return inside a generator sets the final done value
  • Expecting for...of to work on a plain object that lacks Symbol.iterator

See Also

functions array-methods javascript-event-loop