Functional JavaScript — Generators

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

JavaScript is partly a functional language.

To learn JavaScript, we got to learn the functional parts of JavaScript.

In this article, we’ll look at JavaScript generators.

Callback Hell

If we some functions like:

let sync = () => {
  //..
}
let sync2 = () => {
  //...
}
let sync3 = () => {
  //...
}
Enter fullscreen mode Exit fullscreen mode

and each function is synchronous, then we can call it one by one and compose them our way.

But if they’re async, then we can’t call one by one.

An async function may have a callback to let us call the callback when we get the result:

let async1 = (fn) => {
  //...
  fn( /* result data */ )
}
let async2 = (fn) => {
  //...
  fn( /* result data */ )
}
let async3 = (fn) => {
  //...
  fn( /* result data */ )
}
Enter fullscreen mode Exit fullscreen mode

Then if we want to call them sequentially, we’ve to write something like:

async1(function(x) {
  async2(function(y) {
    async3(function(z) {
      //...
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

As we can see, there’s a lot of nesting in our code.

We got to make this cleaner.

Generators

We can clean up this code with generators.

To create a generator function, we can write:

function* gen() {
  return 'foo';
}
Enter fullscreen mode Exit fullscreen mode

A generator function is indicated by the function* keyword.

Then we can call it to create a generator:

let generator = gen();
Enter fullscreen mode Exit fullscreen mode

The generatorResult object has the next method to return the value we yielded.

The returned object has the value and done properties.

So we can write:

generator.next().value
Enter fullscreen mode Exit fullscreen mode

and we get 'foo' .

Calling next for a second time will return an object with the value being undefined .

The yield keyword is a new keyword that will bring value to the next method.

The value of each yield statement will return in the sequence they’re listed.

yield pauses the execution of the function and sends back the result to the caller.

Then next time next is called, the next yield statement is run.

So if we have:

function* gen() {
  yield 'first';
  yield 'second';
  yield 'third';
}

let generator = gen();
Enter fullscreen mode Exit fullscreen mode

The first generator.next() call returns:

{value: "first", done: false}
Enter fullscreen mode Exit fullscreen mode

Then 2nd call returns:

{value: "second", done: false}
Enter fullscreen mode Exit fullscreen mode

The 3rd call returns:

{value: "third", done: false}
Enter fullscreen mode Exit fullscreen mode

The 4th call returns:

{value: undefined, done: true}
Enter fullscreen mode Exit fullscreen mode

which indicates that the generator has no more values to return.

The done property indicates whether the generator bis done with returning all the values.

We know when to stop calling next once done is true .

Passing Data to Generators

We can also pass data to generators.

To do that, we create a generator function with a yield statement that has no value after it.

For example, we can write:

function* fullName() {
  const firstName = yield;
  const lastName = yield;
  console.log(`${firstName} ${lastName}`);
}
Enter fullscreen mode Exit fullscreen mode

We have the yield statement without a value after it, so it’ll accept values we pass into next .

Then we can use it by writing:

const fn = fullName();
fn.next()
fn.next('mary')
fn.next('jones')
Enter fullscreen mode Exit fullscreen mode

We create the generator function.

Then we call next to start the generator.

Once we did that, we can start passing values into the generator.

We call:

fn.next('mary')
Enter fullscreen mode Exit fullscreen mode

to pass in a value to the first yield statement.

Then we do the same with the 2nd one with:

fn.next('jones')
Enter fullscreen mode Exit fullscreen mode

Once we did that, we get 'mary jones' from the console log.

Async Code and Generators

The async and await syntax is created based on generators.

For instance, we can use it by writing:

const getData = async () => {
  const res = await fetch('https://api.agify.io/?name=michael')
  const data = await res.json();
  console.log(data);
}
Enter fullscreen mode Exit fullscreen mode

We have the async and await syntax.

await pauses the execution of getData until the result is present.

So it acts like yield .

Now we run our async code line by line.

The only difference is that await only works with promises.

Conclusion

We can use generators to return items in sequence.

The function is paused after a result is returned and resumed when the next result is requested.

The async and await syntax for promises is based on the generator syntax.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .