JS Generator Function and yield keyword
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:
- What is a Generator Function and yield keyword
- Basic example
- How to stop generator function and not to return all values from it
- Throwing an exception
- Passing arguments into .next() function
- 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:
- Generator Function will have the same type as a normal function
- 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.