Generator
Normally, functions run to completion, that is, once a function starts running, it finishes before anything else can interrupt.
function foo() {
for (let i = 0; i < 10000; i++) {
console.log(i)
}
}
setTimeout(() => {
console.log('Hello')
}, 1)
foo()
// `Hello` might be logged after 1 millisecond- A generator can pause itself in mid-execution, and can be resumed either right away or at a later time.
- When it's paused, the generator can return a value, and the controlling code that resumes it can also send a value back in.
Declare
function *foo() { .. }
function* foo() { .. }
function * foo() { .. }
function*foo() { .. }Execute
function *foo(x, y) {
//..
}
foo(5, 10) // don't need to add asterisk when executing Behind the back
function *foo() {
//..
}
let it = foo()
console.log(it)
// Object [Generator] {}
// to start/advance(推進) *foo(), call it.next()
Use yield to pause and return/receive value
function *foo() {
for (let i = 0; i < 3; i++) {
let x = yield i
console.log(`x: ${x}`)
}
}
let it = foo()
let a = it.next()
console.log(`a: ${a.value}`)
let b = it.next()
console.log(`b: ${b.value}`)
let c = it.next()
console.log(`c: ${c.value}`)
let d = it.next()
console.log(`d: ${d.value}`)
// a: 0
// x: undefined
// b: 1
// x: undefined
// c: 2
// x: undefined
// d: undefined
Use yield to pause and return/receive value
function *foo() {
let x = 0
for (let i = 1; i <= 3; i++) {
x += yield i
console.log(`x: ${x}`)
}
}
let it = foo()
let a = it.next(100) // 第一次的 next(),參數沒有作用
console.log(`a: ${a.value}`)
let b = it.next(a.value)
console.log(`b: ${b.value}`)
let c = it.next(b.value)
console.log(`c: ${c.value}`)
let d = it.next(c.value)
console.log(`d: ${d.value}`)
// a: 1
// x: 1
// b: 2
// x: 3
// c: 3
// x: 6
// d: undefined
// 在 call it.next() 時, generator 會在 yield 的地方 pause
// 等到下一次的 it.next() 時,generator 才會從上次 yield 的地方把 next 傳入的參數 assign 並繼續往下跑
// 所以第一次的 next() 所傳的參數沒有用Low precedence
let x = yield 3 + 2
// same as `let x = yield (3 + 2)`
// will yield 5, and assign x with any params passed in
let x = (yield 3) + 2
// will yield 3 first
// then assign x with any params passed in plus twoYield delegation
function *foo() {
yield *[1, 2, 3] // not the same as `yield [1,2,3]`
}
let it = foo()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: undefined, done: true }- yield * requires an iterable
- Invokes that iterable's iterator, and delegates its own host generator's control to that iterator until it's exhausted.
Yield delegation
let arr1 = [1, 2, 3]
produce its iterator
( let it = arr1[Symbol.iterator( ) )
yield *
it.next()
whoever call
the Object[Generator].next( )
Yield delegation
That example has the same meaning as this one
function *foo() {
yield 1
yield 2
yield 3
}
function *bar() {
// bar delegates its control to the iterator(generator) of foo by calling foo()
yield *foo()
}
let it = bar()
console.log(it)
console.log(it.next())
console.log(it.next())
console.log(it.next())Iterator control
function *foo () {
// start up from initial paused state -> 3
// but there's no yield waiting in the beginning, so 'first' will not be assigned to any variable -> 4
// paused by the first yield and return 1 -> 5
let x = yield 1
// resumed by the second it.next() and assign 'second' to x -> 8
// paused by the second yield and return 2 -> 9
let y = yield 2
// resumed by the third it.next() and assign 'third' to y -> 12
// paused by the third yield and return 3 -> 13
let z = yield 3
// resumed by the fourth it.next() and assign 'fourth' to z -> 16
// log output -> 17
console.log(`x: ${x}, y: ${y}, z: ${x}`)
// no more yield, return undefined -> 18
}
let it = foo() // assign the generator object to it
// starting up the generator from its initial paused state -> 1
// pass 'first' as param -> 2
let first = it.next('first')
// get { value: 1, done: false } from first yield and assign to first -> 6
// pass 'second' as param -> 7
let second = it.next('second')
// get { value: 2, done: false } from second yield and assign to second -> 10
// pass 'third' as param -> 11
let third = it.next('third')
// get { value: 3, done: false } from third yield and assign to third -> 14
// pass 'fourth' as param -> 15
let fourth = it.next('fourth')
// get { value: undefined, done: true } and assign to third -> 14Iterator control
function *foo() {
yield *[1,2,3]
}
let it = foo()
for (let v of it) {
console.log(v)
}
// 1
// 2
// 3Normally, you don't get return from generator
function *foo() {
let a = 10
let b = 20
return a + b
}
let it = foo()
console.log(it) // Object[Generator] {}But you can do that with yield*
function *foo() {
yield 1
yield 2
yield 3
return 4
}
function *bar() {
let x = yield *foo()
console.log(`x: ${x}`)
yield 5
}
// create the generator object for bar()
// but becuase of yield delegation, it will hand over the control to foo's iterator
let it = bar()
// so, when you call bar.next(), it will implicitly call foo().next() until foo() is exhausted
console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: 3, done: false }
// since foo() is exhausted, it return the value to bar, and x is assigned
// log `x: 4`
console.log(it.next()) // { value: 5, done: false }
console.log(it.next()) // { value: undefined, done: true }Early completion
function *foo() {
yield 1
yield 2
yield 3
}
let it = foo()
console.log(it.next()) // { value: 1, done: false }
console.log(it.return(42)) // { value: 42, done: true }
console.log(it.next()) // { value: undefined, done: true }Early Abort
function* foo () {
yield 1
yield 2
yield 3
}
let it = foo()
console.log(it.next()) // { value: 1, done: false }
try {
it.throw('Error!')
} catch(e) {
console.log(e) // Error!
}
console.log(it.next()) // { value: undefined, done: true }Independent generators
function* foo () {
yield 1;
yield 2;
yield 3;
}
let it1 = foo();
it1.next(); // { value: 1, done: false }
it1.next(); // { value: 2, done: false }
let it2 = foo();
it2.next(); // { value: 1, done: false }
it1.next(); // { value: 3, done: false }
it2.next(); // { value: 2, done: false }
it2.next(); // { value: 3, done: false }
it2.next(); // { value: undefined, done: true }
it1.next(); // { value: undefined, done: true }Making generator if not using ES6
function foo () {
// ..
return {
next: function (v) {
// ..
}
// we'll skip `return(..)` and `throw(..)`
}
}Generator
By ianyshuang
Generator
Slide for You don't Know JS: Generator
- 247