一、co函数是什么  

 co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。短小精悍只有短短200余行,就可以免去手动编写Generator 函数执行器的麻烦

二、co函数怎么用

  举个栗子就能清楚的知道如何使用co函数

 function* gen(){
var f1 = yield func1;
var f2 = yield fnuc2;
//sth to do
};

手动执行和co函数执行的写法如下

 // 手动执行
var wp = gen()
gen.next()
gen.next()
// co 函数
var co = require('co')
co(gen)

两者的差别应该一眼就能看出来了。

三、co函数如何实现自动执行

co用promise的特性,将整个Generator函数包装在一个promise下,利用Generator的next链,循环调用co方法将不同的next任务分别包装为不同的子promise。根据next的状态来执行不同的resolve,进而实现自动执行。

基本流程如下图,忽略我渣渣的画图能力


    具体如何实现,下面一起看下源码

四、co源码解析

为了更好的分析,还是对源码的方法进行分类主要有以下两类:

4.1  辅助函数

辅助函数很好理解了,主要是用来做类型判断,参数解析等功能的。直接看代码

 /**
* obj 是否promise
* 利用promise.then存在且为function
*/ function isPromise(obj) {
return 'function' == typeof obj.then;
} /**
* obj是否Generator
* 利用Generator的next 和 throw 两属性为Fuction的特点加以判断
*/ function isGenerator(obj) {
return 'function' == typeof obj.next && 'function' == typeof obj.throw;
} /**
* 是否Generator方法
* 利用constructor的name和displayName属性。
* @example
* var a = {}
* a.constructor === Object
* a.constructor.name // "Object"
*/ function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
} /**
* 判断是否干净对象
* 利用constructor 属性。
* @example
* Object.constructor === Object
*/ function isObject(val) {
return Object == val.constructor;
}

  4.2  功能函数,这里就按调用流程看

co:入口函数,将传入的函数先做类型判断然后返回一个promise对象

/**
* 执行generator,返回一个promise对象
* 首次调用即将整个fn包在主promise中
*/ function co(gen) {
// 当前执行环境上下文
var ctx = this;
// 获取参数
var args = slice.call(arguments, 1); return new Promise(function(resolve, reject) {
/**
* 生成一个gen实例
* 如果不是gen函数,结束并执行resolve回调
*/
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen); //.... });
}

  这里首先生成一个gen函数实例,如果gen非generater函数,直接执行resolve

然后调用onFulfilled函数,即先执行一次generater.next,将value作为后面的执行的参数

 function onFulfilled(res) {
var ret;
try {
/**
* 执行next,获取执行结果
*/
ret = gen.next(res);
} catch (e) {
return reject(e);
}
// 调用实现自动执行的关键函数,next
next(ret);
return null;
}

然后就到了实现自动执行的关键函数next

  题外话,既然是实现了自动调用,无非是递归和迭代来调用执行函数,next函数就负责该部分内容

 /**
* next函数的实现
* 一句话总结:如果为done,则value传入resolve并执行,否则调用co生成子promise,继续执行
*/
function next(ret) {
// done return 并执行reslove,即回到上层promise如果为主promise,则执行完成
if (ret.done) return resolve(ret.value);
// 执行结果创建子promise,不同数据结构实现方式不同
var value = toPromise.call(ctx, ret.value);
// 将onFulfilled作为resolve传入,确保子promise执行完成之后回到主promise。
// 这样next执行链创建完成
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}

  看到这里可能会问,具体的循环调用是在哪里实现的,不要急咱们再看下toPromise的实现:

 /**
* 将obj转换成promise
* obj无非为以下几种类型:
* 1、非object的基本数据类型===>直接返回
* 2、promise===>直接返回
* 3、Generator对象和方法===> co调用
* 4、thunk函数===>thunkToPromise
* 5、Object ===>objectToPromise
*/ function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
// 主要看这里,能转化为generator函数的最终都要再次调用co函数,生成子promise,这样就完成了循环调用
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
if ('function' == typeof obj) return thunkToPromise.call(this, obj);
if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
if (isObject(obj)) return objectToPromise.call(this, obj);
return obj;
}

  这里就是将各种类型转化为对应的promise,然后执行。到此co的整体流程就结束了。其他的如何实现这里就不做讲述了,完整的源码解析请移步co函数库源码解析查看。

  上面就是我研究co源码之后的一些个人体会,希望能对其他人有所帮助。

参考文章:http://es6.ruanyifeng.com/#docs/generator

Generator函数执行器-co函数库源码解析的更多相关文章

  1. andorid jar/库源码解析之Butterknife

    目录:andorid jar/库源码解析 Butterknife: 作用: 用于初始化界面控件,控件方法,通过注释进行绑定控件和控件方法 栗子: public class MainActivity e ...

  2. andorid jar/库源码解析之Bolts

    目录:andorid jar/库源码解析 Bolts: 作用: 用于链式执行跨线程代码,且传递数据 栗子: Task.call(new Callable<Boolean>() { @Ove ...

  3. andorid jar/库源码解析之EventBus

    目录:andorid jar/库源码解析 EventBus: 作用: 用于不同Activity,Service等之间传递消息(数据). 栗子: A页面:onCreate定义   EventBus.ge ...

  4. andorid jar/库源码解析之Dagger/Dagger2

    目录:andorid jar/库源码解析 Dagger.Dagger2: 作用: 1.用于解耦Activity和业务逻辑 2.在使用业务的时候,不需要重复编写new代码. 3.当业务变化的时候,不需要 ...

  5. andorid jar/库源码解析之okhttp3

    目录:andorid jar/库源码解析 Okhttp3: 作用: 用于网络编程(http,https)的快速开发. 栗子: // okHttpClient定义成全局静态,或者单例,不然重复new可能 ...

  6. andorid jar/库源码解析之okio

    目录:andorid jar/库源码解析 Okio: 作用: 说白了,就是一个IO库,基于java原生io.来进行操作,内部做了优化,简洁,高效.所以受到了一部分人的喜欢和使用 栗子: 读写文件. p ...

  7. andorid jar/库源码解析之retrofit2

    目录:andorid jar/库源码解析 Retrofit2: 作用: 通过封装okhttp库,来进行web通讯,并且使用动态代理的方式,来调用接口地址,通过回调赋值结果. 栗子: 定义一个接口,用于 ...

  8. andorid jar/库源码解析之zxing

    目录:andorid jar/库源码解析 Zxing: 作用: 生成和识别,二维码,条形码. 栗子: 生成二维码,赋值到ImageView上 QRCodeWriter qrCodeWriter = n ...

  9. andorid jar/库源码解析之错误提示

    目录:andorid jar/库源码解析 错误: 错误1: Error: Static interface methods are only supported starting with Andro ...

随机推荐

  1. UiAutomator2.0入门

    总是听说UiAutomator这个框架,但从来没有使用过.找了篇入门,实践一下.实践之后感觉,uiautomator写测试代码,还是有点费劲.接口名比较多,比较长.网易的atx里使用的uiautoma ...

  2. Qt ------ WAV 音频文件播放

    1.用 QFile 打开 WAV 文件,读出文件头信息,看看是否符合音频播放设备的要求 QAudioDeviceInfo m_audioOutputDevice;//可以获取音频输出设备的信息,比如哪 ...

  3. python BeautifulSoup

    之前解析LXML,用的是XPath,现在临时被抓取写爬虫,接人家的代码,看到用的是BeautifulSoup,稍微学了下,也挺好用的,简单记录下用法,有机会做下和Xpath的对比测试 初始化 from ...

  4. bzoj千题计划239:bzoj4069: [Apio2015]巴厘岛的雕塑

    http://www.lydsy.com/JudgeOnline/problem.php?id=4069 a!=1: 从高位到低位一位一位的算 记录下哪些位必须为0 dp[i][j] 表示前i个数分为 ...

  5. SQL语句(十二)分组查询

    (十二)分组查询 将数据表中的数据按某种条件分成组,按组显示统计信息 查询各班学生的最大年龄.最小年龄.平均年龄和人数 分组 SELECT <字段名表1> FROM <表名> ...

  6. 转----MarkdownPad2.5 注册码

    经测试可用 User: Soar360@live.com 授权: GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImDHzWdD6xhMNLGVpbP2 ...

  7. JavaScript Cookies取值

    http://www.w3school.com.cn/js/js_cookies.asp

  8. 大话C#中能使用foreach的集合的实现

    大家都知道foreach的语法:foreach(var item in items){ Console.Writeln(item);} 通过这样一个简单的语句,就能实现遍历集合items中的所有元素. ...

  9. zabbix user parameters和Loadable modules的使用方法介绍

    目录 需求 实现 原理 前端配置 后端配置 shell实现 python实现 C实现 需求: 采集主机的-/+ buffers/cache  free的数据 实现: 采集/proc/meminfo中的 ...

  10. decimal模块

    简介 decimal意思为十进制,这个模块提供了十进制浮点运算支持. 常用方法 1.可以传递给Decimal整型或者字符串参数,但不能是浮点数据,因为浮点数据本身就不准确. 2.要从浮点数据转换为De ...