Skip to content

Instantly share code, notes, and snippets.

@simon-tiger
Last active October 27, 2021 11:47
Show Gist options
  • Save simon-tiger/c3e978071b63e5ea0bb0905d2a2450b3 to your computer and use it in GitHub Desktop.
Save simon-tiger/c3e978071b63e5ea0bb0905d2a2450b3 to your computer and use it in GitHub Desktop.

Generator Functions

What are generator functions?

Generator functions are basically functions that you can pause at some point. You can define a generator function with function*:

function* Generator() {
  // code goes here
}

I like to named my generator functions with a capital, for reasons that will become apparent later, but generally they are named with a lowercase letter.

Then you can use the yield keyword inside the function

function* Generator() {
  yield 10;
  yield 20;
}

yield is like return in that it pauses the function, but the difference is that the code can resume to the function later.

Now that you defined a generator function, you can make a generator out of it:

const gen = Generator();

This is also the reason I like to name generator functions with a capital. It's like when you make an instance of the class, except it's a generator function, not a class.

Calling next() on the generator will execute the code in the function until the next yield.

console.log(gen.next());
// Output: { value: 10, done: false }

Notice that it doesn't return the yielded value directed, but in an object. It has the yielded value, and whether the function is done.

Now if you call next() again, it will yield 20:

console.log(gen.next());
// Output: { value: 20, done: false }

Now if you call it again, the value will be undefined but the done property will be true because the generator function is done:

console.log(gen.next());
// Output: { value: undefined, done: true }

Infinite loops

Infinite loops are ok in generator functions, but only if you have a yield in them.

function* Generator() {
  let num = 0;
  while (true) {
    num++;
    yield num;
  }
}

This is because it doesn't run the whole code at once. It first sets num to 0. Then it enters the loop, it increments num, and then it yields it and stops.

yield*

Ok, this is the reason I think generator functions are cool, they're very easy to visualize algorithms with, especially recursive ones. This is because of yield*.

yield* allows you to automatically yield everything from a different generator (or the same one if you're feeling recursive). The reason I think this is useful is the following: say you're visualizing an algorithm like quicksort. It is really difficult to implement in a normal loop, because it's fundamentally not an iterative algorithm. With a generator function, on the other hand, it's just so much easier.

function* Quicksort(lo, hi) {
  yield* Partition(lo, hi); // Partition() can set a mid variable which is where the pivot is
  yield* Quicksort(lo, mid-1);
  yield* Quicksort(mid+1, hi);
}

Now you can step through every step of the quicksort algorirthm by just using next().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment