Show Buttons
Share On Facebook
Share On Twitter
Share On Google Plus
Share On Linkdin
Share On Reddit
Share On Stumbleupon
Contact us
Hide Buttons

Generators and Yield in es6

This arti­cle is the fifth install­ment of our ES6 tuto­r­ial series. In this arti­cle, we will con­tinue where we left off from our pre­vi­ous arti­cle on generators.


Two way com­mu­ni­ca­tion using yield and next

Yield is a spe­cial key­word. Not only does it let you pause a function’s exe­cu­tion, it also ‘emits’ a value which can be con­sumed by its caller. This way, the yield key­word can be used as both — a data con­sumer and a data producer.

Take a look at the fol­low­ing example.

function* doSomething() {
  // This sends 'hello' to the caller and pauses execution
  // of this function until next() is invoked
  yield 'hello';

  // This sends 'world' to the caller and pauses execution
  // of this function until next() is invoked
  // Notice the two-way communication happening at this point
  var lastInput = yield 'world';

  console.log(lastInput);
}

var gen2 = doSomething();
// This prints out the value returned at the first yield and pauses
console.log(gen2.next().value); // Prints 'hello'

// This resumes execution after the first yield until the next
// yield is encountered
console.log(gen2.next().value); // Prints 'world'

// This resumes execution after the second yield but there is 
// nothing more to execute
gen2.next('The end.');

----

Yield mul­ti­ple values

Within a gen­er­a­tor func­tion yield* can be used to recur­sively iter­ate over an iterable.

function* doSomething() {
  yield* ['hello', 'world'];
}

var gen = doSomething();

console.log(gen.next().value); // Prints 'hello'
console.log(gen.next().value); // Prints 'world'

In fact, not only can you yield prim­i­tives, you can also yield other gen­er­a­tors. That pri­mar­ily because a gen­er­a­tor object is an iter­able in itself. For example.

function* generatorFoo() {
  yield 1;
  yield 2;
}

function* generatorBar() {
  // First Create the generator object by invoking the function
  // then yield it.
  yield* generatorFoo();
  yield 'I am bar!'
}

var genObj1 = generatorFoo();
// Iterate on the generator object directly
console.log(genObj1.next().value); // Prints 1
console.log(genObj1.next().value); // Prints 2

var genObj2 = generatorBar();
// Iterate on the generator object directly that in itself yields
// the values of another generator
console.log(genObj2.next().value); // Prints 1
console.log(genObj2.next().value); // Prints 2
console.log(genObj2.next().value); // Prints I am bar!

You can also use the reg­u­lar for-of loop to do the same.

function* greet() {
  yield 'hello';
  yield 'world';
}

for (let message of greet()) {
  console.log(message);
}

Using the ‘return’ state­ment from within a generator.

The value of a return state­ment is usu­ally unus­able inside of a gen­er­a­tor con­structs except when used with yield*. For example.

function* greet() {
  yield 'hello';
  yield 'world';
  return 1;
}

// The for-of construct doesnt print the value thats returned
for (let message of greet()) {
  console.log(message);
}

Now take a look at the fol­low­ing example

function* greet1() {
  yield 'hello';
  yield 'world';
  return 1;
}

function* greet2() {
  // The result of invoking yield* is the return value.
  var returnValue = yield* greet1();
  console.log(returnValue);
}

for (let message of greet2()) {
  console.log(message);
}

Gen­er­a­tor invo­ca­tion and arguments

There are 2 tricky aspects when deal­ing with gen­er­a­tors that lend it some spe­cial behaviour.

–Invok­ing a gen­er­a­tor func­tion doesn’t exe­cute the func­tion right away.
–The first invo­catin of next() on a gen­er­a­tor object does not accept any arguments.

These two aspect directly impact how and when you pass argu­ments to a generator.

function* player(name) {
  console.log('name received'); // (A)
  var life = 1000;
  yield 'Hello' + name; // (B)
  yield 'You have a life of a ' + life + ' years'; // (C)
}

// Lets create our generator object
// Since our function acceps a name, we need
// to pass this name right away
var p = player('Goku');
// Notice that although it seems like you executed
// the function above, it does not print the console statement
// at line (A)


// Invoking next() for the first time causes 
// line (A) to be printed and then
// pauses at line (B).
// Notice how the first next() does not take any argument
console.log(p.next().value); // Prints 'name received' followed by 'Hello Goku'(which is this console log)

// Line (C) gets printed then paused
console.log(p.next().value); // Prints 'You have a life of a 1000 years'

// Nothing more to execute
console.log(p.next().value); // Prints undefined because there isint any next value

Yield’ and gen­er­a­tor scope

Last but not the least, yield can only be used when its in the scope of gen­er­a­tor. For exam­ple, the fol­low­ing code will not work.

function* generatorFoo() {
  [1, 2].forEach(function (item) {
    yield item; // This wont work because the scope has changed
  });
}

Hope you found the tiny exper­i­ments in the exam­ples use­ful. I would like add more tiny exper­i­ments to this post, so if you have any more use cases that could be included here, just drop me a comment.


Ryan Sukale

Ryan is just a regular guy next door trying to manage his life and finances.

You may also like...

  • Oliver Sis­son

    // Line © gets printed then paused
    console.log(p.next().value); // Prints ‘Hello Goku’

    Are you sure it’s Line © that gets printed?

    • tuto­ri­al­hori­zon

      Thanks I fixed it!