Generator Functions in JavaScript
Generator Function
Generator Function:
- Its a function but a bit different from the normal functions,
- Normal functions run their entire code at once and return a value,
- But a generator function has breakpoints in between marked by
"yield",
- And you can pause the code execution at yield,
- To create a generator function, you need to add a (*) after function keyword,
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
Generator Function
Generator Function:
- When you call a generator function, you get an iterable object instead of function's return value,
- the iterable object has the same ".next()" method,
- to get any value from generator function, you need to call the ".next()" method,
- on every call to ".next()", the code runs till it encounters a yield in the function,
- it returns the value at yield, and then execution pauses, till "next" is called again
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
const generator = generateSequence();
const one = generator.next();
console.log(JSON.stringify(one));
// {value: 1, done: false}
Generator Function
Generator Function:
- inside the function you only define the value with yield that you want at next call,
- but yield returns an object of shape:
{value: 1, done: false},
- the return statement of a generator function returns an object like this:
{value: 3, done: true},
- once the done: true is returned, you cannot call .next anymore
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
const generator = generateSequence();
const one = generator.next();
console.log(JSON.stringify(one));
// {value: 1, done: false}
const two = generator.next();
console.log(JSON.stringify(two));
// {value: 2, done: false}
const three = generator.next();
console.log(JSON.stringify(three));
// {value: 3, done: true}
Generator Function
Real use-cases:
- you can write a for loop and control it from outside
function* name() {
for(let value = 0; value <= 10; value++) {
yield value;
}
}
const iter = name()
iter.next()
// {value: 0, done: false}
Generator Function
Generators are iterable
- calling a generator function returns an iterable object,
- so you can write for...of loop on that iterable object
- Issue: for loop will not get the value in return statement, as the loop ends when iterable returns done: true
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1, then 2
}
function* generateSequence() {
yield 1;
yield 2;
yield 3;
}
let generator = generateSequence();
for(let value of generator) {
alert(value); // 1, then 2, then 3
}
Generator Function
Using generator functions to create iterables:
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
return {
current: this.from,
last: this.to,
next() {
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
}
};
console.log([...range]);
const range = {
from: 1,
to: 5,
*[Symbol.iterator]() {
// a shorthand for [Symbol.iterator]: function*()
yield this.from++
yield this.from++
yield this.from++
yield this.from++
yield this.from++
}
};
- Any function that's a generator function, will return an object with ".next" method,
- calling that ".next" method, will return an object: {value: , done: }
Generator Function
Generator composition:
- yield*,
- a generator function that yields another generator function,
- inside a generator function, you can use yield* to call another generator function,
- when generatePasswordCodes is called, it returns the iterable object,
- the execution pauses at first yield* (line 8), till generateSequence generator function is not done with it's for...of loop
- when line 8 is done, the code moves to next yield* on line 11
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
function* generatePasswordCodes() {
// 0..9
yield* generateSequence(48, 57);
// A..Z
yield* generateSequence(65, 90);
// a..z
yield* generateSequence(97, 122);
}
let str = '';
for(let code of generatePasswordCodes()) {
str += String.fromCharCode(code);
}
console.log(str);
Generator Function
Passing through the yield:
- when you use assignment operator (=) on yield,
- you can call the .next method twice for each yield
- the first .next call yields the right hand side value of yield
- the second .next call can be made with an argument, that argument is assigned as value to the variable assigned to yield,
- when you call .next method second, with argument, it yields the value passed as argument
function* gen() {
// Pass a question to the outer
// code and wait for an answer
let result = yield "2 + 2 = ?";
console.log({result});
}
let generator = gen();
let question = generator.next().value;
generator.next(4);
Generator Function
// ANother more elaborate example
function* gen() {
let ask1 = yield "2 + 2 = ?";
alert(ask1); // 4
let ask2 = yield "3 * 3 = ?"
alert(ask2); // 9
}
let generator = gen();
alert( generator.next().value ); // "2 + 2 = ?"
alert( generator.next(4).value ); // "3 * 3 = ?"
alert( generator.next(9).done ); // true
Generator Function
Stopping the generator in between:
-
the iterable object, has a method on it named "return",
-
you can call the "return" method that returns the object with
{done: true},
-
you can also call the return method with an argument, and that argument is returned in the object value key,
-
calling the .next afterward will keep returning the same object
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
g.next(); // { value: 1, done: false }
g.return('foo'); // { value: "foo", done: true }
g.next(); // { value: undefined, done: true }
Generator Function
Throwing error from generator
-
the iterable object, has a method on it named ".throw",
-
you can call the ".throw" method that throws error from generator,
-
the generator function stops when the error is thrown
function* generate() {
let result = yield "2 + 2 = ?"; // Error in this line
}
let generator = generate();
let question = generator.next().value;
try {
generator.throw(new Error("The answer is not found in my database"));
} catch(e) {
alert(e); // shows the error
}
Generator Functions in JavaScript
By Yash Priyam
Generator Functions in JavaScript
- 94