Categories
Modern JavaScript

Best of Modern JavaScript — Yield

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.

The yield Keyword

The yield keyword can only be used in generator functions.

For example, we can write:

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

But we can’t write:

function* genFn() {
  ['foo', 'bar'].forEach(x => yield x);
}

We’ll get a syntax error.

Recursion with yield*

The yield* keyword lets us call another generator function within a generator function.

For instance, we can write:

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

function* bar() {
  yield 'x';
  yield* genFn();
  yield 'y';
}

const gen = bar()

to call the genFn generator function within the bar generator function.

So if we use it by writing:

const arr = [...gen];

then arr is [“x”, “foo”, “bar”, “baz”, “y”] .

Calling genFn returns an object but it doesn’t run genFn .

The yield* keyword runs the generator function when it’s time for its items to be yielded.

yield* Considers End-of-Iteration Values

yield* ignores return values.

For example, if we have:

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

function* bar() {
  yield 'x';
  yield* genFn();
  yield 'y';
}

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

Then arr is [“x”, “foo”, “bar”, “y”] , yield* skipped over 'baz' since it’s returned instead of yield.

We can use yield* to recursively call generator functions.

So we can easily use it to return items from a tree structure.

For instance, we can create a Tree class with various branch nodes:

class Tree {
  constructor(value, left = null, center = null, right = null) {
    this.value = value;
    this.left = left;
    this.center = center;
    this.right = right;
  }

  *[Symbol.iterator]() {
    yield this.value;
    if (this.left) {
      yield* this.left;
    }

    if (this.center) {
      yield* this.center;
    }

    if (this.right) {
      yield* this.right;
    }
  }
}

left , center , and right are all generator functions created from the Tree class.

So we can write:

const tree = new Tree('a',
  new Tree('b',
    new Tree('c'),
    new Tree('d'),
    new Tree('e')),
  new Tree('f'));

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

And we get:

a
b
c
d
e
f

The value is the value for the tree node itself.

And we populate the nodes with the Tree constructor.

Generators as Observers

Generators can also be data observers.

We use it to send values with the next method.

The next method keeps returning values from then generator until the values run out.

For instance, if we have:

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

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

And we get:

{value: "foo", done: false}

We can also call next with an argument to return that index of what’s yielded with the generator.

For example, we can write:

function* genFn() {
  console.log(yield);
  console.log(yield);
  console.log(yield);
}

const gen = genFn();
console.log(gen.next('a'));
console.log(gen.next('b'));
console.log(gen.next('c'));

We use the yield keyword to get the value passed in from the argument we passed into next .

The value return from the next method is:

{value: undefined, done: false}

value is undefined and done is false .

yield keyword without an operand gets the value from the next method.

Conclusion

The yield and yield* keyword have many uses.

yield can return values and also takes them from the next method.

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.