sailei1 / blog

1 stars 0 forks source link

你不知道的JS -生成器 笔记 #82

Closed sailei1 closed 5 years ago

sailei1 commented 5 years ago

生成器 生成器就是一类特殊的函数,可以一次或多次启动和停止,并不一定非要完成。

function *foo(x) {
          var y = x * (yield);
          return y;
      }
      var it = foo( 6 );
      // 启动foo(..) 
      it.next();
      var res = it.next( 7 );
      res.value;     // 42

首先,传入 6 作为参数 x。然后调用 it.next(),这会启动 foo(..)。 在foo(..)内部,开始执行语句var y = x ..,但随后就遇到了一个yield表达式。它 就会在这一点上暂停 foo(..)(在赋值语句中间!),并在本质上要求调用代码为 yield 表达式提供一个结果值。接下来,调用it.next( 7 ),这一句把值7传回作为被暂停的 yield 表达式的结果。 所以,这时赋值语句实际上就是var y = 6 7。现在,return y返回值42作为调用 it.next( 7 ) 的结果。 注意,这里有一点非常重要。

yield 会导致生成器在执行过程中发送出一个值,这有点类似于中间的 return。

第一个 next(..) 总是启动一个生成器,并运行到第一个 yield 处。不过,是第二个 next(..) 调用完成第一个被暂停的 yield 表达式,第三个 next(..) 调用完成第二个 yield, 以此类推。

yield 并不会阻塞整个程序,它只是暂停或阻塞了生成器本身的代码。

迭代器 迭代器是一个定 义良好的接口,用于从一个生产者一步步得到一系列值。

// for..of循环需要
[Symbol.iterator]: function(){ return this; },

try catch 如果在生成器内有 try..finally 语句,它将总是运行,即使生成器已经外部结束。

function *something() {
         try {
               var nextVal;
             while (true) {
                 if (nextVal === undefined) {
                  nextVal = 1; }else {
                     nextVal = (3 * nextVal) + 6;
                    }
                 yield nextVal;
             }
            }finally {
             console.log( "cleaning up!" );
         }
}

 var it = something();
     for (var v of it) {
         console.log( v );
     if (v > 500) {
       console.log(it.return( "Hello World" ).value);
       // break;  break也会触发
     }
}
// 1 9 33 105 321 969 
// cleaning up!
// Hello World
// for..of 循环内的 break 会触发 finally 语句。但是,也可以在外部通过return(..) 手工终止生成器的迭代器实例

获得 Promise 和生成器最大效用的最自然的方法就是 yield 出来一个 Promise,然后通过这个 Promise 来控制生成器的迭代器。

function foo(x,y) {
    return request(
             "http://some.url.1/?x=" + x + "&y=" + y //返回一个promise
         );
    }
function *main() {
         try {
             var text = yield foo( 11, 31 );
             console.log( text );
         }
         catch (err) {
             console.error( err );
} }

    var it = main();
     var p = it.next().value;
// 等待promisep决议 
   p.then(
         function(text){
             it.next( text );
          },
         function(err){
             it.throw( err );
           }
       );

async 与 await async 函数。最后,我们不再 yield 出 Promise,而是用 await 等待它决议 如果你 await 了一个 Promise,async 函数就会自动获知要做什么,它会暂停这个函数(就 像生成器一样),直到 Promise 决议。

Promise 并发

function run(gen) {
         var args = [].slice.call( arguments, 1), it; // 在当前上下文中初始化生成器
         it = gen.apply( this, args );
           // 返回一个promise用于生成器完成
         return Promise.resolve() 
       .then( function handleNext(value){ 
        // 对下一个yield出的值运行
        var next = it.next( value );
            return (function handleResult(next){ 
            // 生成器运行完毕了吗?
            if (next.done) {
                             return next.value;
                                 }
                        // 否则继续运行 
                        else {
                         return Promise.resolve( next.value )
                             .then(
                            // 成功就恢复异步循环,把决议的值发回生成器 
                            handleNext,
                // 如果value是被拒绝的 promise, 
                // 就把错误传回生成器进行出错处理 
                function handleErr(err) {
                          return Promise.resolve(
                                it.throw( err )).then( handleResult );
                }
                ); }
            })(next);
         });
       }

function *foo() {
// 让两个请求"并行"
 var p1 = request( "http://some.url.1" );
 var p2 = request( "http://some.url.2" );
// 等待两个promise都决议 
 var r1 = yield p1;
 var r2 = yield p2;
 var r3 = yield request(
     "http://some.url.3/?v=" + r1 + "," + r2
);
 console.log( r3 );
}
// 使用前面定义的工具run(..) 
run( foo );
//如果p1先决议,那么yield p1就会先恢复执行,然后等待yield p2恢复。如果p2先决 议,它就会耐心保持其决议值等待请求,但是 yield p1 将会先等待,直到 p1 决议。
不管哪种情况,p1 和 p2 都会并发执行,无论完成顺序如何,两者都要全部完成,然后才 会发出 r3 = yield request..Ajax 请求。

生成器委托

function *foo() {
         console.log( "*foo() starting" );
         yield 3;
        yield 4;
       console.log( "*foo() finished" );
}

function *bar() {
    yield 1;
    yield 2;
    yield *foo();
    yield 5;
}
var it = bar();
it.next().value; // 1
it.next().value; // 2
it.next().value; //  *foo() starting
                 // 3          
it.next().value; // 4 *foo() finished
it.next().value; // 5
//一旦 it 迭代器控制消耗了整个 *foo() 迭代器,it 就会自动转回控制 *bar()。
//yield *暂停了迭代控制,而不是生成器控制。当你调用*foo()生成器 时,现在 yield 委托到了它的迭代器。

yield 可以通过JS闭包的迭代器来模拟实现

// request(..)是一个支持Promise的Ajax工具 
function *foo(url) {
      // 状态1
         try {
             console.log( "requesting:", url );
             var TMP1 = request( url );
        // 状态2
           var val = yield TMP1; 
           console.log( val );
         }
         catch (err) {
      // 状态3
       console.log( "Oops:", err ); 
       return false;
   }
}
转换成es5 

function foo(url) { 
// 管理生成器状态 
   var state;
// 生成器变量范围声明 
    var val;
     function process(v) {
         switch (state) {
             case 1:
                 console.log( "requesting:", url );
                 return request( url );
             case 2:
                 val = v;
                 console.log( val );
                 return;
            case 3:
            var err = v;
            console.log( "Oops:", err );
            return false;
            } 
        }
        // 构造并返回一个生成器 
             return {
               next: function(v) { 
                if (!state) {  // 初始状态
                                 state = 1;
                                 return {
                                     done: false,
                                     value: process()
                                 };
                }else if (state == 1) { // yield成功恢复
                                 state = 2;
                                 return {
                                    done: true,
                                     value: process( v )
                                 };
                } else {  // 生成器已经完成
                                 return {
                                     done: true,
                                     value: undefined
                                 };
                } 
               },
               "throw": function(e) {
                // 唯一的显式错误处理在状态1
                if (state == 1) {
                                 state = 3;
                                 return {
                                     done: true,
                                     value: process( e )
                                     };
                }else {
                throw e; // 否则错误就不会处理,所以只把它抛回  
                  }
                } 
       };
    }