Categories
Modern JavaScript

Best of Modern JavaScript — Generators as Producers

Spread the love

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

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

Generators as Iterators

We can use generator functions as iterators.

For instance, we can write:

function* genFn() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}

const gen = genFn();

console.log(gen.next());
console.log(gen.next());

to create a generator function and a generator.

genFn is the generator function as indicated with the function* keyword.

The function returns a generator with the genFn call.

Then we call next to get the next item on the list.

The yield keyword lets us return the value with the next method call.

Therefore, we get:

{value: "foo", done: false}
{value: "bar", done: false}

to get the yielded items via the value property.

We can also access the yielded values with the for-of loop.

For example, we can write:

function* genFn() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}

const gen = genFn();

for (const x of gen) {
  console.log(x);
}

Then we get:

foo
bar
baz

logged.

The for-of loop can access the values from a generator.

The spread operator lets us convert a generator to an array by extracting the yielded values and putting it there:

function* genFn() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}

const gen = genFn();
const arr = [...gen];

And destructuring can also work with generators by assigning the yielded values as values of the variables on the left side:

function* genFn() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}

const [x, y] = genFn();

Then we get that x is 'foo' and y is 'bar' .

Returning from a Generator

We can use the return statement from a generator.

For instance, we can write:

function* genFn() {
  yield 'foo';
  yield 'bar';
  return 'baz';
}

Then we can invoke our generator by writing:

const gen = genFn();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

From the console log, we can see that the values are:

{value: "foo", done: false}
{value: "bar", done: false}
{value: "baz", done: true}

The return statement set done to true .

However, most other const5ructurs that work with iterable objects ignore the return value.

For instance, if we have the for-of loop:

function* genFn() {
  yield 'foo';
  yield 'bar';
  return 'baz';
}

const gen = genFn();
for (const x of gen) {
  console.log(x);
}

Then we get:

foo
bar

logged.

And if we have the spread operator:

function* genFn() {
  yield 'foo';
  yield 'bar';
  return 'baz';
}

const gen = genFn();
const arr = [...gen];

Then arr is [“foo”, “bar”] .

Throwing Exception from a Generator

We can throw exceptions from a generator.

For instance, we can write:

function* genFn() {
  throw new Error('error');
}

const gen = genFn();
console.log(gen.next())

We have a generator function that throws an error.

When we call it and then call next on the returned generator, we see the error logged in the console.

Conclusion

We can return items and throw errors in a generator function.

They can also produce values we can use.

Leave a Reply

Your email address will not be published.

If you like the content of this blog, subscribe to my email list to get exclusive articles not available to anyone else.