Regular function returns single value or nothing Generator can return multiple values, one after another by calling yield Generator function needs to have a * symbol: function* Generator function creates a generator object [object Generator] Code execution does not start on generator creation, but on .next() call Result of .next() is an object with two properties: {value: 1, done: false} Syntax
function* generateSequence1() {
yield 1
yield 2
yield 3
}
let generator1 = generateSequence1() // generateSequence {<suspended>}
let one = generator1.next() // {value: 1, done: false}
let two = generator1.next() // {value: 2, done: false}
let three = generator1.next() // {value: 3, done: false}
let four = generator1.next() // {value: undefined, done: true}
// with return
function* generateSequence2() {
yield 1
yield 2
return 3
}
let generator2 = generateSequence2()
let one = generator2.next() // {value: 1, done: false}
let two = generator2.next() // {value: 2, done: false}
let three = generator2.next() // {value: 3, done: true}
let four = generator2.next() // {value: undefined, done: true}
Generator is iterable
// without return
for(let value of generator1) {
console.log(value) // 1, 2, 3
}
// with return
for(let value of generator2) {
console.log(value) // 1, 2
}
Spread syntax
[...generator1] // [1, 2, 3]
Iterable object based on generator
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() {
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};
alert( [...range] ); // 1,2,3,4,5
Generator inside generator - yield* yield* directive delegates the execution to another generator
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
function* generatePasswordCodes() {
yield* generateSequence(48, 57) // 0..9 // for (let i = 48; i <= 57; i++) yield i;
yield* generateSequence(65, 90) // A..Z
yield* generateSequence(97, 122) // a..z
}
let str = ''
for(let code of generatePasswordCodes()) {
str += String.fromCharCode(code)
}
alert(str) // 0..9A..Za..z
Pass values from next to yield yield not only returns the result to the outside, but can pass the value inside the generator
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 = ?" // first next() should be always w/o arg
alert( generator.next(4).value ) // "3 * 3 = ?"
alert( generator.next(9).done ) // true
Throw error into generator Outer code may pass error into a generator
function* gen() {
try {
yield "start"
alert("not reach here")
yield "end"
} catch(e) {
alert(e) // shows the error
}
}
let generator = gen()
alert(generator.next().value)
generator.throw(new Error("error"))
alert(generator.next().value)
// start // error // undefined