zoukankan      html  css  js  c++  java
  • Generator & Co

    Generator

    搬运自
    http://es6.ruanyifeng.com/#docs/generator

    如果没有babel等环境也可以在线体验
    可以在http://www.es6fiddle.net/ OR https://babeljs.io/repl/ 在线编译ES6

    Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

    带有星星的函数就是Generator

    function* gen(x){....}
    

    generator的关键字是yield和next yield关键字只能在带有星星的函数中使用

    yield x+2; //yield的意思是产出 这里表示生产 x+2 这个值
    

    而调用g.next() 则会一直执行到yield函数之前(也包括yield语句)或者说函数执行将会在yield 这句话这里卡住, 直到有个next()调用
    来个最简单的例子

    
    function* gen(){
      yield 1;
      yield 2;
    }
    var g0 = gen();
    console.log(g0.next()); //{"value":1,"done":false} 返回的是一个对象  包括yield后面的结果 以及这个函数是否执行完毕
    console.log(g0.next());
    console.log(g0.next());
    
    //执行结果
    // {"value":1,"done":false}
    // {"value":2,"done":false}
    // {"done":true}
    

    再来个复杂一点的, 深刻理解一下yield的执行过程

    var y = 'heheh';
    
    function* gen(x) {
      console.log('start');
      //因为第一句就是yield  所以函数到这里若没有调用 g.next()是没有执行的
      //第一次调用 g.next() 则执完到第一个yield 之前的语句 以及计算需要yield的值
      //也就是第一次调用 g.next() 执行了 第一句log('start) 和 yield x+2
      y = yield x + 2;
      //yield的意思是产出 这里表示生产 x+2 这个值
      //看起来好像把 yield产出的值赋值给了y 实际上不是
      //y的值是在下一次(也就是第二次调用的时候由 g.next() 传入的)
    
    
      console.log('2nd');
      yield 2333;
      //第二次调用 g.next() 执行到第二个yield语句这里
      // console.log('3rd');
      // return y;
    }
    
    
    var g = gen(1);//此时并没有执行
    console.log(g.next());
    console.log('y is '+ y); //y仍是最初始的值
    //输出
    //start
    // { value: 3, done: false }
    //g.next().value 的结果是 yield后的值
    console.log(g.next('haYYY')); // {"value":2333,"done":false}
    console.log('y is '+ y); // y is haYYY
    
    //第三次调用 整个函数执行完毕
    console.log(g.next());
    //输出的结果包括
    //3rd
    //{"value":"haYYY","done":true}  //第三次调用next()  其value就是*函数的返回值(因为已经木有yield语句了)
    console.log('g is ',g); //{}
    

    g = gen(1) 实际上这个函数没有执行
    直到g.next()的时候才真正执行

    我传入的x是2 所以这里yield x+2 实际上是 yield 3;
    然后暂停了
    第一个next 开始执行 语句一行一行的执行直到遇到一个yield停止
    所以第一个得到yield 的结果 { value: 3, done: false }

    那么y呢 y的值是 3 吗?
    不是的 y的值是下一个next传入的值
    刚才说到了第一个next就执行到 yield 3 这里
    后来又 g.next('y') 这里传入了一个参数 'y'
    这个字符串就是 yield x+1 这个表达式的返回值

    第n个 next的参数 就是 第 n-1 个 yield的返回值
    所以如果你在第一个next() 就传值的话 是没有意义的
    PS 一个generator对象有n句 yield 总需要n+1句next() 才能走完整个流程

    如何用Generator来实现异步呢

    实际上就是把异步的函数放在 yield 关键字之后
    yield后面可以接的对象有 function, promise, generator, array, object

    另外需要借助co的支持

    搬运https://cnodejs.org/topic/542953d42ca9451e1bf3c251
    那么就可以像同步操作那样的写法来写异步函数

    co(...) 使用时应向co传入一个generator对象

    var co = require('co');
    co(function* (){
        var now = Date.now();
        yield delay800;
        console.log(Date.now() - now);
    });
    
    function delay800(cb) {
        setTimeout(cb, 800);
    }
    

    这个例子的感受可能不明显 有了co和generator, 数据查询甚至可以变成这样

    co(function *(){
        var rs = yield db.query('select url from xxx');
        rs.forEach(rs){
            var content = yield getUrl(rs.url);
            ...
        }
    })();
    

    co的原理

    co的作用是可以使异步的函数放在 yield关键字之后 然后整个异步流程的代码写法可以像同步函数一样
    并不是实现了一个generator

    观察使用co的代码 co接受一个参数 该参数是一个generator函数
    co(generator){ ... }
    再看一般generator的调用 在一个generator函数执行后返回一个对象g 通过g.next()不断调用下一个函数
    所以co要做的事情 就是接受generator 并且通过每一次next()的返回值判断是否执行完毕来看是否需要继续调用 g.next()

    下面是co最简单的实现

    参考 https://cnodejs.org/topic/53474cd19e21582e740117df

    co(function* (){
        var now = Date.now();
        yield delay800; //yield后面应该接一个函数对象
        yield delay500;
        console.log(Date.now() - now);
    });
    
    function delay800(callRecurence) {
        setTimeout(callRecurence, 800);
    }
    function delay500(callRecurence){
      setTimeout(function(){
        //some async ...
        callRecurence();
      },500)
    };
    
    
    function co(generator) {
      var gen = generator();
      function next(err, result) {
        if(err){
          return fn(err);
        }
        var step = gen.next(result); //{value: fn , done: false}
        if (!step.done) {//没有执行完毕 继续next
    
          //执行异步函数  step.value 就是yield后的异步函数
          //next 就是当前next(err,result)函数  通过递归的方式不断调用next达到多次调用 gen.next()
          step.value(next);
        } else {
          // console.log('done!');
        }
      }
      next();
    }
    

    Async

    http://www.ruanyifeng.com/blog/2015/05/async.html

    Async可以让你像使用同步函数一样来使用异步
    await 后面是异步函数 该函数应当返回一个Promise
    await只能存在于有async修饰的函数中

    
    async function getAsyncRs(name) {
      var symbol = await asyncFun1(name);
      console.log(symbol);
      var stockPrice = await asyncFun2(symbol);
      return stockPrice;
    }
    
    getAsyncRs('goog').then(function(result) {
      console.log(result);
    });
    
    function asyncFun1(name) {
      return new Promise(function(resolve) {
        setTimeout(function() {
          resolve('async1' + name);
        }, 500);
      });
    }
    function asyncFun2(name) {
      return new Promise(function(resolve) {
        setTimeout(function() {
          resolve('async222' + name);
        }, 1000);
      });
    }
    
    
  • 相关阅读:
    入门activiti-------1简单运行
    JS对象、构造器函数和原型对象之间的关系
    myeclipse配背景色
    maven的pom.xml文件错误
    oracleXE简易版---使用基础
    ognl表达式注意事项
    Executors、ExecutorService、ThreadPoolExecutor
    ThreadPoolExecutor
    Phaser相位(工具的实战案例使用)
    ForkJoin
  • 原文地址:https://www.cnblogs.com/cart55free99/p/4893498.html
Copyright © 2011-2022 走看看