Javascript Generator Function Basics

We'll learn about javasciprt feature not many people use or talk about. It's generator function. So what is it?

Generator function is a function that can generate more than one value. So how do we make it?

function* greet() {
  yield 'Hello World!'
}

So what's different with regular function? First, instead of function we define it with asterisk function* to indicate this isn't your regular function. Then instead of return a value, we yield it. The special thing about yield is that the function can be yield-ed more than once!

function* greet() {
  yield 'Hello world!'
  yield 'Hello again!'
  yield 'Hello, I am bored'
}

Now how do we get those values? First we can turn it into generator object by invoking it.

let word = greet()
console.log(word) // Object [Generator] {}

And then we can get the values by invoking the next method from the word variable.

let word = greet()
console.log(word.next()) // { value: 'Hello world!', done: false }

Notice that the value logged turned into an object with 'Hello world!' there in value property. And we have another property called done with value false, what is that? The done property indicates whether all the value from greet function has been yielded or not. In a simple way, it tells you:

Hey this is the value you wanted, but I'm not done yet. I still have more.

So if we want to get the other values, we can do it again and again, you get the idea:

let word = greet()
console.log(word.next()) // { value: 'Hello world!', done: false }
console.log(word.next()) // { value: 'Hello again!', done: false }
console.log(word.next()) // { value: 'Hello, I am bored!', done: false }

But wait, is that it? Kind of. Because you can still call next after that. It's just... not too necessary. But hey it's your code.

...
console.log(word.next()) // { value: 'Hello, I am bored!', done: false }
console.log(word.next()) // { value: undefined, done: true }
console.log(word.next()) // { value: undefined, done: true }
console.log(word.next()) // { value: undefined, done: true }

Working with loop

Now assuming the generator function is not ours (maybe it's a package) how do we know how may values can we yield? We can get all of them by using for ... of loop.

for(let w of word) {
  console.log(w)
}
/*
Hello world!
Hello again!
Hello, I am bored
*/

Endless value generator

Now say that we want to get the first 5 numbers that are divisible by 3 (3, 6, 9, 12, 15). But when i want the first 7 numbers, 18 and 21 will come along. We can make our function like this:

function* divisibles3() {
  let num = 1
  while(true) {
    if(num % 3 === 0) yield num
    num++
  }
}

let n = divisibles3()

We can get the values by calling it as many as we want:

// 1. First 5
console.log(n.next().value) // 3
console.log(n.next().value) // 6
console.log(n.next().value) // 9
console.log(n.next().value) // 12
console.log(n.next().value) // 15

// 2. Fisrt 7
for(let i = 0; i < 5; i++) {
  console.log(n.next().value) // 3, 6, 9, 12, 15, 18, 21
}

Or better, we can make it dynamic so the function can take any divisible:

function* divisibles(div) {
  let num = 1
  while(true) {
    if(num % div === 0) yield num
    num++
  }
}

Now we can get any first numbers divisible by any number:

// 1. First 3 divisible by 4
let n = divisibles(4)
for(let i = 0; i < 3; i++) {
  console.log(n.next().value) // 4, 8, 12
}
// 2. Fisrt 4 divisible by 7
let n = divisibles(7)
for(let i = 0; i < 4; i++) {
  console.log(n.next().value) // 7, 14, 21, 28
}

Source:

27