Generator & Co

本文深入探讨ES6中的Generator函数,介绍其基本语法如yield和next,以及如何利用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);
  });
}

转载于:https://www.cnblogs.com/cart55free99/p/4893498.html

标题基于Python的自主学习系统后端设计与实现AI更换标题第1章引言介绍自主学习系统的研究背景、意义、现状以及本文的研究方法和创新点。1.1研究背景与意义阐述自主学习系统在教育技术领域的重要性和应用价值。1.2国内外研究现状分析国内外在自主学习系统后端技术方面的研究进展。1.3研究方法与创新点概述本文采用Python技术栈的设计方法和系统创新点。第2章相关理论与技术总结自主学习系统后端开发的相关理论和技术基础。2.1自主学习系统理论阐述自主学习系统的定义、特征和理论基础。2.2Python后端技术栈介绍DjangoFlask等Python后端框架及其适用场景。2.3数据库技术讨论关系型和非关系型数据库在系统中的应用方案。第3章系统设计与实现详细介绍自主学习系统后端的设计方案和实现过程。3.1系统架构设计提出基于微服务的系统架构设计方案。3.2核心模块设计详细说明用户管理、学习资源管理、进度跟踪等核心模块设计。3.3关键技术实现阐述个性化推荐算法、学习行为分析等关键技术的实现。第4章系统测试与评估对系统进行功能测试和性能评估。4.1测试环境与方法介绍测试环境配置和采用的测试方法。4.2功能测试结果展示各功能模块的测试结果和问题修复情况。4.3性能评估分析分析系统在高并发等场景下的性能表现。第5章结论与展望总结研究成果并提出未来改进方向。5.1研究结论概括系统设计的主要成果和技术创新。5.2未来展望指出系统局限性并提出后续优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值