《红宝书》 |生成器

生成器拥有在一个函数块内暂停和恢复代码执行的能力

生成器函数

生成器的形式是一个函数,而函数名前面加上一个*表示它是一个生成器。只要是可以定义函数的地方,就能定义生成器:

//生成器函数声明
function * generatorFn(){}

//生成器函数表达式
let generatorFn=function * (){}

//作为对象字面量方法的生成器函数
let foo={
    * generatorFn(){}
}

//作为类实例方法的生成器函数
class Foo{
    * generatorFn(){}
}

//作为类静态方法的生成器函数
class Foo{
    static * generatorFn(){}
}

生成器的*不受两侧空格影响箭头函数不能用来定义生成器函数

生成器对象

调用生成器函数会产生一个生成器对象:

//生成器函数
function * generatorFn(){}
//生成器对象
let g=generatorFn()

生成器对象实现了iterator接口,即生成器对象也可称为是迭代器对象。生成器一开始是处于暂停执行(suspended)状态的,调用next()方法能让其开始或恢复执行。

next()的返回值类似于迭代器,具有donevalue属性。函数体为空的生成器函数调用一次next()就会让生成器对象到达{done:true}状态:

function * generatorFn(){}
let g=generatorFn()

console.log(g[Symbol.iterator]) //ƒ [Symbol.iterator]() { [native code] } --表明实现了iterator接口
console.log(g[Symbol.iterator]())  //generatorFn {<suspended>}    --返回一个生成器,打印值表示一开始是暂停执行

console.log(g)  //generatorFn {<suspended>} --表示一开始是暂停执行
console.log(g.next) //f next() { [native code] }    --表示生成器对象具备next方法
console.log(g.next()) //{value: undefined, done: true}  --调用next方法恢复执行

yield基本概念

yield关键字可以让生成器停止和开始执行。生成器函数在遇到之后执行会停止,函数作用域的状态会被保留,需要在生成器对象上调用next()方法恢复执行:

function * generatorFn(){
    yield
}
let g=generatorFn()
console.log(g.next())   //{value: undefined, done: false}   --遇到了yield停止执行
console.log(g.next())   //{value: undefined, done: true}    --接着触发next,恢复执行

通过next()返回的对象中,value属性表示生成器函数的返回值,默认值为undefined。我们可通过yieldreturn指定生成器函数的返回值,不同的点在于,yield返回的对象处于done:false状态,而return返回的对象处在done:true状态:

function * generatorFn(){
    yield "one";
    yield "two"
    return "three"
}
let g=generatorFn()
console.log(g.next())   //{value: "one", done: false}
console.log(g.next())   //{value: "two", done: false}
console.log(g.next())   //{value: "three", done: true}

在生成器对象上调用next()是互不干扰的:

function * generatorFn(){
    yield "one";
    yield "two"
    return "three"
}
let g1=generatorFn()
let g2=generatorFn()
console.log(g1.next())   //{value: "one", done: false}
console.log(g2.next())   //{value: "one", done: false}
console.log(g2.next())   //{value: "two", done: false}
console.log(g1.next())   //{value: "two", done: false}

yield关键字只能在生成器函数内部调用,否则抛错:

//无效
function * generatorFn(){
    function fnname(){
        yield;
    }
}

//无效
function * generatorFn(){
    const fnname=()=>{
        yield;
    }
}

yield应用

可迭代对象

生成器对象显式调用next()的用处不大,把它当做可迭代对象用起来更方便:

//无效
function * generatorFn(){
    yield "one";
    yield "two"
    yield "three"
}
//这里的generatorFn()就相当于一个生成器对象,因为调用了generatorFn生成器函数
for( const item of generatorFn()){
    console.log(item)
}

自定义迭代对象

生成一个迭代器,执行指定的次数:

//自定义可迭代对象:生成n个yield
function * nTime(n){
    //当n减为0时,相当于flase,退出循环
    while(n--){
        yield;
    }
}

//创建迭代器:nTime(3)
for(let n of nTime(3)){
    console.log('hello')
}

实现范围和填充数组

function * range(start,end){
    while(end>start){
        yield start++
    }
}
for(const x of range(4,7)){
    console.log(x)
}

实现输入输出

yield会接受传给next()的值:

function * state(initial){
    console.log(initial)
    console.log(yield)
    console.log(yield)
}
let g=state('hello')
console.log(g.next('one'))  //hello --遇到yield停止执行,所以返回hello
console.log(g.next('two'))  //two   --再次调用next恢复执行,yield成功接受值并打印
console.log(g.next('three'))    //three

产生可迭代对象

可以使用yield*让它能够迭代一个可迭代对象,从而一次产出一个值(星号两侧的值不影响其行为):

function * generatorFn(){
    yield* [1,2,3]
    yield* [4,5]
}
let g=generatorFn()
for(const x of g){
    console.log(x)
}
//1
//2
//3
//4
//5

生成器作为默认迭代器

生成器函数和默认迭代器(Symbol.iterator)被调用后都会产生迭代器,所以生成器函数适合作为默认迭代器:

class Foo{
    constructor(){
        this.value=[1,2,3]
    }
    *[Symbol.iterator](){
        yield* this.value
    }
}

const f=new Foo()
for (const x of f){
    console.log(x)
}

提前终止生成器

可用过return()throw()提前终止生成器:

  • return()会强制生成器进入关闭状态,提供给return 的值就是终止迭代器对象的值

    function * generatorFn(){
          yiled* [1,2,3] 
    }
    let g=generatorFn()
    console.log(g)              //generatorFn {<suspended>}
    console.log(g.return(4))    //{value: 4, done: true}
    console.log(g)              //generatorFn {<closed>}
    console.log(g.next())       //{value: undefined, done: true}    --只要进入关闭状态,后续调用next都返回done:done
    
  • throw()会在暂停的时候将一个提供的错误注入到生成器对象中,如果错误未被处理,生成器就会关闭:

    function * generatorFn(){
          yiled* [1,2,3] 
    }
    let g=generatorFn()
    
    console.log(g)    //generatorFn {<suspended>}
    try{
        g.throw('foo')
    }catch(e){
        console.log(e)    //foo
    }
    console.log(g)    //generatorFn {<closed>}
    

    如果生成器函数内部处理了这个错误,生成器就不会关闭。错误处理会跳过对应的yiled:

    function * generatorFn(){
          for(const x of [1,2,3]){
              try{
                  yield x;
              }catch(e){
                  console.log('error:'e)
              }
          }
    }
    let g=generatorFn()
    
    console.log(g.next())     //{value: 1, done: false}
    g.throw('foo')            //error:foo
    console.log(g.next())     //{value: 3, done: false}
    

    相关文章:生成器函数的异步应用

posted @ 2021-02-17 15:43  sanhuamao  阅读(200)  评论(0编辑  收藏  举报