生成器是特殊的函数。生成器函数在执行时能暂停,后面又能从暂停处执行。 show me the codingnode
function* zcGenerator() { // 这就是生成器
yield 'ak-47'
}
复制代码
const zcInterator = zcGenerator()
复制代码
调用生成器并不会执行函数,而是会返回一个迭代器,迭代器用来控制生成器的执行。调用迭代器的next()
方法,会向生成器请求一个值,生成器内的语句执行到第一个yield
的位置,并返回yield
后跟的值,此时生成器异步挂起执行,直到下次再请求值,生成器恢复执行,如此循环下去,直到最后生成器执行完毕。 next()
方法返回一个对象{value: '', done: ''}
, value
表示本次yield
返回的值,done
表示生成器后续时候仍是有yield语句,即生成器是否执行完毕。
从执行上下文的角度来理解迭代器,建立迭代器的时候,生成器入栈,初始化参数(若是有的话),执行完毕后出栈,而且销毁当前执行环境,可是迭代器仍然保存着对他的引用,因此该环境并不会销毁,这个东西和闭包很像。换个角度来讲,生成器须要恢复执行,因此环境不能销毁,保存在迭代器上。bash
function* weaponGenerator() {
yield 'ak47'
yield 'm16'
}
const weaponIterator = weaponGenerator()
console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
复制代码
使用yield*
表示将执行权交到另外一个生成器函数(当前函数暂停执行)闭包
function* weaponGeneratorInner() {
yield 'g56'
yield 'h58'
}
function* weaponGenerator() {
yield 'ak47'
yield* weaponGeneratorInner()
yield 'm16'
}
const weaponIterator = weaponGenerator()
console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'g56', done: false}
console.log(weaponIterator.next()) // {value: 'h58', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
复制代码
交互方式有三种dom
function* parGenerator(weapon) {
yield weapon
}
const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
复制代码
next()
传递参数,与生成器交互 next()
的参数会传递给当前生成器正在yield
的值,所以,第一次调用next()
,生成器并无处于挂起的值,因此传递参数没有任何意义,调用完第一个next()
方法,生成器开始执行,碰见第一个yield
,函数挂起,而且返回当前值;第二次调用next('j-30')
,j-30
会替换掉当前yield
的值j-20
,并继续往下执行,将newWeapon
赋值为j-30
,生成器执行完毕。function* parGenerator(weapon) {
const newWeapon = yield weapon
return newWeapon
}
const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
console.log(parIterator.next('j-30')) // { value: 'j-30', done: true }
复制代码
注意:由于经过
next()
传递参数只能传递给当前生成器yield
的值,所以第一次调用next()
传递参数没有意义,因此,若是想为生成器提供一个初始值,能够向生成器函数穿第一个参数做为初始值。异步
throw
方法与生成器交互function* throwGenerator() {
try {
yield 'good'
} catch (err) {
console.log(err)
}
}
const throwInterator = throwGenerator()
console.log(throwInterator.next())
console.log(throwInterator.throw('bad'))
复制代码
throw
与next
传参相似,只能对当前生成器yield
的值throw
,所以,在建立迭代器以后当即调用throw
没有意义(会报错),换个角度来讲,只有代码执行的时候才能够try-catch
,在throw
以前没有执行生成器,try-catch
也就没什么用。函数
注意:
throw
必须在next
以后调用,由于在调用第一个next
以后,生成器在第一个yield
处挂起,只有在请求下一个值或者throw
的时候才会继续日后执行try...catch...
,在throw的时候,生成器从新入栈,从yield 'good'
的地方继续日后执行,生成器会将yield 'good'
做为一个错误抛出,被catch
抓住。ui
function* idGenerator() {
let id = 0
while (true) {
yield ++id
}
}
const idIterator = idGenerator()
const id1 = idIterator.next().value
console.log(id1)
const id2 = idIterator.next().value
console.log(id2)
复制代码
由于yield会暂时挂起函数执行,因此while
不会无限循环 2. 遍历dom节点spa
function* elementGenerator(element) {
yield element
element = element.firstElementChild
while (element) {
elementGenerator(element)
element = element.nextElementSibling
}
}
for (let elementGeneratorElement of elementGenerator()) {
console.log(elementGeneratorElement.nodeName)
}
复制代码