Skip to content
Lesson 11 of 15

Step 1 of 5 · Reading · ~4 min

Learn

Generators and Regex

Iterators and Generators

Behind for...of, spread ([...x]) and destructuring (const [a, b] = x) sits one small protocol. Learn it and you can make your own objects work with all of them. A generator is a function that writes that protocol for you and can pause in the middle. By the end of this lesson you can read and write both, and use generators to produce values lazily, one at a time, even from an infinite sequence.

The iteration protocol

An iterator is any object with a next() method that returns { value, done }. An iterable is any object with a method named [Symbol.iterator]() that returns an iterator. Arrays and strings are iterable, and so are Map and Set, which a later lesson covers.

javascript

for...of simply calls [Symbol.iterator]() once, then calls next() until done is true. You can make any class iterable the same way:

javascript

Destructuring pulls only as many values as it needs, so nothing is computed ahead of demand.

Generators: functions that pause

Put * after function and use yield. Calling a generator function does not run its body. It returns a generator object, which is both an iterator and an iterable:

javascript

Each next() runs the body until the next yield, hands that value out, and freezes with all local variables intact. A return x ends the sequence (x appears in the final { value: x, done: true }, but for...of and spread ignore it).

Lazy sequences compose

A generator can take an iterable and yield from it, so small pieces chain into a pipeline in which every value flows through the whole chain before the next is asked for:

javascript

Because values are produced on demand, a generator can describe an infinite sequence (while (true) yield ...) safely, as long as the consumer stops. The classic mistake is then asking for all of it: [...infinite()] never finishes and runs the machine out of memory. Take a limited number instead.

Stopping early, and other details

  • break, return or a throw inside a for...of calls the iterator's optional return() method, which makes a generator run its finally block:
javascript
  • yield* other delegates to another iterable: yield* "ab" yields "a" then "b".
  • A generator object is single use. Once finished it yields nothing more: [...gen, ...gen] repeats nothing the second time.

Common mistakes

  • Spreading or looping over an infinite generator without a stop.
  • Reusing an exhausted generator and getting an empty result.
  • Expecting steps() to run the body. It only creates the generator.
  • Using yield inside a callback such as forEach(x => yield x). It is a syntax error; yield belongs directly to the generator function.
  • Checking the limit after pulling one value too many, so a lazy source does work nobody wanted.

Your exercise

Write three small generators that plug into a lazy pipeline over an infinite source, and the starter counts how many values were really pulled. The last point above is exactly what that count checks.

Up nextRegular ExpressionsGenerators and Regex

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…