Generator函数与协程 - Go语言中文社区

Generator函数与协程


协程

协程有点像函数,又有点像线程,它的运行流程大致如下。

  1. 协程A开始执行
  2. 协程A执行到一半,进入暂停,执行权转移到协程B
  3. (过了一段时间)协程B交换执行权
  4. 协程A恢复执行

Generator 函数

Generator 函数是 ES6 对协程的实现,但属于不完全实现。Generator函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator函数。如果是完全执行的协程,任何函数都可以让暂停的协程继续执行。


Generator 函数执行产生的上下文环境,一旦遇到yield命令,就会暂时退出堆栈,但是并不消失,里面的所有变量和对象会冻结在当前状态。等到对它执行next命令时,这个上下文环境又会重新加入调用栈,冻结的变量和对象恢复执行。

  function* gen() {
    console.log(0);
    yield;
    console.log(1);
    yield 'hello';
    console.log(2);
    console.log(yield);
    yield* 'hello';
  }
  let f = gen();
  f.next();//0
  console.log(f.next().value);//1 hello
  console.log(f.next('aaaaa').value);//2 undefined
  console.log(f.next('world').value);//world h
  console.log(f.next('').value);//e
  console.log(f.next('').value);//l
  console.log(f.next('').value);//l
  console.log(f.next('').value);//o
  console.log(f.next(''));//{value: undefined, done: true}
  console.log(f.next(''));//{value: undefined, done: true}

执行generator函数之后,该函数并不会立即执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
每一次调用next()方法,内部指针就开始执行,直到遇到下一个yield表达式位置。(如果为yield*则会对其值进行for of处理)
next方法会返回此时yield的值。注意,yield本身所在行的代码,会在下一次yield才执行,包括next()方法内对yield的赋值。
即,next返回的值是下一个yield或return的值,而next传入的值会给上一个yield。


generator支持迭代器(Iterator),如

function* helloGenerator() {
       yield "hello";
       yield "generator";
       return;
   }
   var h = helloGenerator();
   for(var value of h){
      console.log(value);//"hello","generator"
  }

generator生成的对象,还有其他一些函数,比如throw()用来抛出错误,return()用来定义返回值并终止generator的状态。
以上的三个方法在本质上其实是一样的,他们就是让generator恢复执行,并且使用不同的语句来替代yield语句。

  • next()是将yield表达式替换成一个值
  • throw()是将yield表达式替换成一个throw语句
  • return()是将yield表达式替换成一个return语句
    此时指针不会向下一个yield移动,而是直接{value: undefined, done: true}(此时的value为return的入参)

Generator 用于协程和解决回调地狱

  • 协程,大厨与伙计交替工作
//大厨的活
   function* chef(){
      console.log("fired chicken");//炒鸡
      yield "worker";//交由伙计处理
      console.log("sdd ingredients");//上料
      yield "worker";//交由伙计处理
   }
   //伙计的活
   function* worker(){
       console.log("prepare chicken");//准备工作
       yield "chef";//交由大厨处理
       console.log("stewed chicken");
       yield "chef";//交由大厨处理
       console.log("serve chicken");//上菜
   }
   var ch = chef();
   var wo = worker();
   //流程控制
   function run(gen){
       var v = gen.next();
       if(v.value =="chef"){
          run(ch);
       }else if(v.value =="worker"){
          run(wo);
       }
   }
   run(wo);//开始执行
  • 解决回调地狱

//准备
   function prepare(sucess){
        setTimeout(function(){
             console.log("prepare chicken");
             sucess();
         },500)
   }
 
   //炒鸡
   function fired(sucess){
        setTimeout(function(){
             console.log("fired chicken");
             sucess();
         },500)
   }
   //炖鸡
   function stewed(sucess){
        setTimeout(function(){
             console.log("stewed chicken");
             sucess();
         },500)
   }
   //上料
   function sdd(sucess){
        setTimeout(function(){
             console.log("sdd chicken");
             sucess();
         },500)
   }
   //上菜
   function serve(sucess){
        setTimeout(function(){
             console.log("serve chicken");
             sucess();
         },500)
   }
 
  //流程控制
  function run(fn){
    const gen = fn();
    function next() {
        const result = gen.next();
        if (result.done) return;//结束
        // result.value就是yield返回的值,是各个工序的函数
        result.value(next);//next作为入参,即本工序成功后,执行下一工序
    }
    next();
  };
  //工序
  function* task(){
     yield prepare;
     yield fired;
     yield stewed;
     yield sdd;
     yield serve;
  }
  run(task);//开始执行
版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/7005675f8248
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 13:03:20
  • 阅读 ( 938 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢