Introduction to ES6 Iterators and Generators

Javascript Generator Series

This article is part of a series of articles about ES6 generators. The plan is to explore iterators and generators, starting with what they are and show casing different situations where they can be useful.

  1. Introduction to ES6 Iterators and Generators
  2. Working with Generators
  3. Async/Await with Generators

Iterator basics

Iterators in Javascript are objects that implement next() which returns an object with two properties, done and value.

// Simple Iterator
{
	next: () => ({ done: false, value: 1 })
}

An Iterable is an object that can be iterated over, the informal protocol for an Iterable is an object that defines the Symbol.iterable method which returns an iterator.

// Simple Iterable
{
  [Symbol.iterator]: () => ({ next: () => ({ done: false, value: 1 })})
}

These aren’t very useful since all they do is return an infinite stream of the number 1 when iterated over.

By conforming to the Iterable protocol an object can be used in the for...of construct.

Generator Basics

So, what do Iterator and Iterable have to do with Generators? Turns out, a lot, since Generators conform to both Iterable and Iterator.

In short, generators are coroutines. Here is the short description from MDN:

Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.

When the generator is iterated over, through next(), it executes to the first yield statement. There it will save it’s state and return the value passed to yield. The next time next() is called, the state will be restored and the code will continue executing where it left of. This will continue until the end of the function (or return) where the final value will be returned and the iterator marked as done.

A function is declared a generator by adding an * after the keyword function.

function* aGenerator() {
	yield 1
	yield 2
	yield 3
	return 4
}

const gen = aGenerator()
gen.next() // { value: 1, done: false }
gen.next() // { value: 2, done: false }
gen.next() // { value: 3, done: false }
gen.next() // { value: 4, done: true }

See MDN for more in-depth information about generator functions.

Why are generators interesting

In the next couple of articles, we’ll have a look on various use cases for generators. Starting with infinite generators that generate an infinite stream of values and show how they can be useful in the context of a finite iteration.

In the later articles, we’ll look at more advanced use cases, where we pass values into the generator and even use them for synchronization when doing concurrent programming.