Generator Function:
"yield",
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
let generator = generateSequence();
Generator Function:
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:
{value: 1, done: false},
{value: 3, done: true},
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}
Real use-cases:
function* name() {
for(let value = 0; value <= 10; value++) {
yield value;
}
}
const iter = name()
iter.next()
// {value: 0, done: false}
Generators are iterable
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
}
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++
}
};
Generator composition:
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);
Passing through the yield:
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);
// 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
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 }
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
}