• Generator生成器


    什么是生成器?

    ES6的新语法,并不直接执行逻辑,而是生成一个对象--迭代器,通过调用这个对象的next()方法,来一步步向后走。执行完一个yield关键字就会停止,等待下一次next()调用。

    funtion* myLogin(){
      yield 2;
      yield 5;    
    return 10; }
    var it = myLogin();
    it.next(); // 2
    it.next(); // 5

    当执行到return语句时,迭代器的迭代已经结束。如果生成器中没有return语句,则返回对象的value值为undefined

    return 10; 
    // {value: 10, done: true}

    yield表达式如果用在另一个表达式中,必须放在圆括号中

    console.log('Hello' + (yield 12))
    console.log('Hello' + yield 12) //报错

    next()的参数问题

    next()函数是可以接受一个参数的,其作为上一次yield的返回值。但第一次使用next()函数时,是没有上一次的yield的,并且实际上是来开启遍历器对象的,传参是没有效果的。

    在有些情况下,从第二次使用next()开始传入参数就很有必要了

    function* foo(x) {
      var y = 2 * (yield (x + 1));
      var z = yield (y / 3);
      return (x + y + z);
    }
    
    var a = foo(5);
    a.next() // Object{value:6, done:false}
    a.next() // Object{value:NaN, done:false}
    a.next() // Object{value:NaN, done:true}
    
    var b = foo(5);
    b.next() // { value:6, done:false }
    b.next(12) // { value:8, done:false }
    b.next(13) // { value:42, done:true }

    在以上代码中,第一次next()得到值6,下一次next()如果不传参数,x+1的值就被认为是undefined,后面继续执行结果都为NaN。但是如果传入了参数,比如例子中的12,也就是将x+1赋值为12,y=24,第二次next()的value为8。第三次传入13,y/3的值、z的值就为13,return结果就是5+24+13=42

    如果一定想在第一次调用next()时就传参并生效,则需要在生成器外部包一个函数

    function wrapper(genFunc){
      return funtion(...arg){
         var gen = genFunc(...arg);
         gen.next();    
         return gen;  
      }  
    }
    wrapper(funtion*(){
       console.log(`first input ${yield}`) 
       return 'Done'; 
    })
    
    wrapper().next('lalala')

    因为在调用wrapper函数时,内部返回的函数就已经开启了迭代器,所以可以第一次next()时就传参且生效

    for...of...

    for...of...循环可以自动遍历Generator函数生成的迭代器,不需调用next()方法

    function* foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    for (let v of foo()) {
      console.log(v);
    // 1 2 3 4 5 }

    当遍历器返回的done为true,for..of..就会中止

    除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

    Generator.prototype.throw

    返回的遍历器对象,都有一个throw()方法,当执行出现错误时,抛出异常。这个异常可以被生成器内部的catch捕捉,也可以被全局catch捕捉。外部的全局throw也可以被内部的catch捕捉。如果生成器抛出的异常没有catch捕捉,那么遍历器会中止执行。

    Generator.prototype.return

    返回的遍历器对象,都有一个return()方法。当调用return()方法时,返回的value值就为return的值,done为true,也就是遍历器会中止执行。如果return()中不传参数,返回的value值为undefined

    funtion* gen(){
      yield 1;
      yield 2;
      yield 3;  
    }
    var it = gen();
    it.next(); // {value : 1, done : false}
    it.return(6); // {value : 6, done : true}

    当生成器函数中存在try...finally..代码块,且正好执行到try代码块时,return()会直接开始执行finally中的代码,执行完后返回传入的值,遍历器中止

    function* gen() {
                try{
                    yield 1;
                    yield 2;
                }finally{
                    yield 3;
                    yield 4;
                }
            }
            var it = gen(); 
            it.next();// {value:1,done:false}
            it.return(7); {value:3,done:false
        it.next();// {value:4,done:false}
         it.next();// {value:7,done:true}

    yield*

    如果在一个生成器函数中调用另一个生成器函数,需要在前者函数内部对后者进行遍历

    function* foo() {
      yield 'a';
      yield 'b';
    }
    
    function* bar() {
      yield 'x';
      // 手动遍历 foo()
      for (let i of foo()) {
        console.log(i);
      }
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    }

    ES6中提供yield*解决

    function* foo() {
      yield 'a';
      yield 'b';
    }
    
    function* bar() {
      yield 'x';
      yield* foo();
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    } 
    // x a b y

    如果使用的是yield而不是yield*返回的就是一个遍历器对象,而不是遍历的结果

    当使用yield*遍历的生成器函数内部没有return语句时,就相当于使用for...of...遍历生成器函数。如果有return语句,就需要使用一个变量来接收return的值

    只要被遍历的对象有iterator接口,就可以使用yield*进行遍历

    Generator中的this

    生成器返回的都是遍历器对象,可以得到生成器原型上的属性和方法。但是this并不指向这个遍历器对象。

    异步场景下的使用

    function myAjax(){
            return fetch('https://img.alicdn.com/imgextra/i4/745475881/O1CN01VtOOnR1tJXl7Cnmcy_!!745475881.png');
        }
        function genRunner(genFunc){
            return new Promise(function(resolved, rejected){
                var it = genFunc();
                var innerRunner = function (data) {
                    var val = it.next(data)
                    if (val.done) {
                        resolved(val.value)
                        return
                    }
                    if (val.value) {
                        val.value.then(data => {
                            innerRunner(data)
                        })
                    }else{
                        innerRunner(val.value)
                    }
                }
                innerRunner();
            }
        )};
    
    
        genRunner(function*() {
            // generator函数中发起异步请求
            var serverData = yield myAjax();
            console.log('MyLogic after myAjax');
            console.log('serverStatus:%s',serverData.status);
        }).then(function (message) {
            console.log(message)
        })

    在以上代码中,执行genRunner函数,生成器作为参数传入。genRunner函数中首先调用生成器得到迭代器it,然后调用innerRunner方法。第一次调用时,执行至生成器yield关键字结束,即发起了一个ajax请求,返回了一个promise对象。在innerRunner中,val.value就是这个promise对象,通过.then执行next(),将响应结果传到生成器中,就拿到了最后的状态码。此时innerRunner中的val为{value:undefined, done:true},执行最后的resolved函数,value为undefined,因此输出undefined

  • 相关阅读:
    从jdbc到分层的飞跃
    第二章 变量和数据类型
    s1300新学期伊始的我们
    选择结构总结
    第四章 选择结构(二) Switch结构
    第三章 选择结构(一)
    第二章 变量、数据类型和运算符
    使用Java理解程序逻辑 第1章 初识Java 基本部分
    ES命令基础
    Spring MVC拦截器
  • 原文地址:https://www.cnblogs.com/ashen1999/p/12715741.html
Copyright © 2020-2023  润新知