Introduction to JavaScript iterators and generators

02 Jan, 2021

Iterators and generators introduce the concept of iteration directly into the JavaScript language and provide a mechanism for customizing for...of loops.

Iterators

An iterator is an object that defines a sequence and returns a value after its termination. An iterator is an object that implements a next() method with no arguments that return an object with at least the following 2 properties:

  1. done: A boolean value set to false if the iterator was able to produce the next value in the sequence. It is set to true if the iterator has completed its sequence.
  2. value: A JavaScript value returned by the iterator. It's not required if done is true.
function fibonacciIterator(limit = 10) {
  let current = 1, next = 0;
  let index = 1;

  const iterator = {
    next: function () {
      if (index++ <= limit) {
        [next, current] = [current + next, next];
        return { value: current, done: false };
      }
      return { done: true };
    }
  };

  return iterator;
}

const fb = fibonacciIterator(4);
console.log(fb.next()); // { value: 0, done: false }
console.log(fb.next()); // { value: 1, done: false }
console.log(fb.next()); // { value: 1, done: false }
console.log(fb.next()); // { value: 2, done: false }
console.log(fb.next()); // { done: true }

While custom iterators are useful, they require careful programming since we need to explicitly maintain it's internal state. This is where Generators come in to solve the problem.

Generators

Generators allow us to define an iterative algorithm inside a single function whose execution is not continuous. It generates a sequence of values instead of a single value, unlike normal functions. The Generator function returns a next() method, which returns a value of type {value: any, done: boolean}.

We can create a generator function by using the function* syntax. Instead of using the return keyword, we use the yield keyword. When a generator function comes across the yield keyword, it pauses itself, returning the value next to it. It returns a new generator each time it's called using next().

function* fibonacciGenerator(limit = 10) {
  let current = 1, next = 0;
  let index = 1;

  while (index++ <= limit) {
    [next, current] = [current + next, next];
    yield current;
  }
}

const fb = fibonacciGenerator(4);
console.log(fb.next()); // {value: 0, done: false}
console.log(fb.next()); // {value: 1, done: false}
console.log(fb.next()); // {value: 1, done: false}
console.log(fb.next()); // {value: 2, done: false}
console.log(fb.next()); // {value: undefined, done: true}