JS Generator Function and yield keyword

Oleh Baranovskyi
4 min readMay 30, 2018

Generator Function and yield is a brand new feature in JavaScript provided by EcmaScript 6.

In this article we are going to cover the following topics:

  1. What is a Generator Function and yield keyword
  2. Basic example
  3. How to stop generator function and not to return all values from it
  4. Throwing an exception
  5. Passing arguments into .next() function
  6. Usage of yield* combination

What is a Generator Function

Syntax: Generator Function is similar to normal function by syntax except that it contains * sign like in following example:

function* generatorFn() {...}

In the next example all functions are working in the same way:

function* () {...}
function *() {...}
function* generatorFn() {...}
function *generatorFn() {...}

Type and Constructor:

  1. Generator Function will have the same type as a normal function
  2. Generator Function will have ‘GeneratorFunction’ as prototype
console.log(typeof function* generatorFn() {}); // "function"console.log((function*(){}).constructor.name); // "GeneratorFunction"

yield keyword: is used to pause and resume a generator function.

How it works: Unlike a normal function generator function returns multiple values one by one and when it is invoked only part of code is executed (before yield keyword). Result of return statement will be generator object that implements Iteration Protocols.

Basic Example

In my opinion it is always more understandable in live example so here is a basic example:

let log = console.log;function* gen1() {
yield 1;
yield 2;
yield 3;
}
let iterator = gen1();
log(iterator.next()); // { value: 1, done: false }
log(iterator.next()); // { value: 1, done: false }
log(iterator.next()); // { value: 1, done: false }
log(iterator.next().done); // true

How to stop generator function and not return all values from it

In order to stop generator function we need to invoke .return(val) function. .return(val) takes one argument and that argument will be returned as iteration value.

let log = console.log;function* gen1() {
yield 1;
yield 2;
yield 3;
}
let iterator = gen1();
log(iterator.next()); // 1
log(iterator.return(5)); // 5
log(iterator.next().done); // true

Throwing an exception

Inside of generator function it is possible to manually throw exception by using .throw(err) function. .throw(err) takes one argument that describes an error. Here is an example:

let log = console.log;function* gen1() {
try {
yield 1;
} catch(err) {
console.log(`We got an error in ${err} try/catch block`);
}
try {
yield 2;
} catch(err) {
console.log(`We got an error in ${err} try/catch block`);
}
}
let iterator = gen1();
log(iterator.next());
log(iterator.throw('first'));
log(iterator.throw('second').done);

/*
Result:
=======
{ value: 1, done: false }
We got an error in undefined try/catch block
{ value: 2, done: false }
We got an error in undefined try/catch block
true
*/

Passing arguments into .next() function

Function .next() can take an optional argument and it will be used as yield result, closely take look on the following example:

function* gen() {
console.log('0, start');
console.log(`1, ${yield}`);
console.log(`2, ${yield}`);
console.log(`3, ${yield}`);
}
let iterator = gen();
iterator.next(); // 0, start
iterator.next('apple'); // 1 apple
iterator.next('orange'); // 2 orange
iterator.next('banana'); // 3 banana

and one more example:

let log = console.log;function* gen() {
let a = yield 3;
let b = yield a + 7;
let c = yield b + 20;
yield c + 1;
}
let iterator = gen();
log(iterator.next().value); // 3
log(iterator.next(12).value); // 19
log(iterator.next(11).value); // 31
log(iterator.next(15).value); // 16
log(iterator.next().done); // true

Explanation (second example):

1. We invoke `iterator.next().value` it returns 3 as a result of .next().value and will be paused on first yield, so let a = … still dons’t have a value

2. We invoke `iterator.next(12).value` then first yield inside of .gen function will return 12 and it will be assigned to `a` variable, right after `yield a + 7` will return on top 19 and will be paused on second yield so let b = … still dons’t have a value.

3. We invoke `iterator.next(11).value` then second yield inside of .gen function will return 11 and it will be assigned to `b` variable, right after `yield b + 20` will return on top 31 and will be paused on second yield so let b = … still dons’t have a value.

4. We invoke `iterator.next(15).value` third yield inside of .gen function will return 15 and it will be assigned to `c` variable, right after `yield c + 1` will return on top 16.

5. We invoke `iterator.next().done` and it will return true because last yield already was inked.

Usage of yield* combination

Definition: The yield* expression is used to delegate to another generator or iterable object. Here is an example:

let log = console.log;function* gen1() {
yield 2;
yield 3;
}
function* gen2() {
yield 1;
yield* gen1();
yield 4;
yield 5;
}
let iterator = gen2();
log(iterator.next().value); // 1
log(iterator.next().value); // 2
log(iterator.next().value); // 3
log(iterator.next().value); // 4
log(iterator.next().value); // 5
log(iterator.next().done); // true

Conclusion

Thank you guys for reading. I hope you enjoyed it and learned some new stuff related to JavaScript. Please subscribe and press ‘Clap’ button if you like this article.

--

--

Oleh Baranovskyi
Oleh Baranovskyi

Written by Oleh Baranovskyi

Frontend Lead & Architect | Web community manager https://obaranovskyi.com/

No responses yet