一、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

co源码解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

  10. HashMap 源码解析

    HashMap简介: HashMap在日常的开发中应用的非常之广泛,它是基于Hash表,实现了Map接口,以键值对(key-value)形式进行数据存储,HashMap在数据结构上使用的是数组+链表. ...

随机推荐

  1. C#工作笔记

    没想到一个Java后端开发还要负责C#桌面程序,我感觉有点方.不过方归方,活还是要干的.简单记录下学到的一些知识点. 1.引用API函数 namespace Demo { class MyUtil { ...

  2. Servlet Filter 中init和destroy问题

    测试源码如下: package com.FilterTest.Filter; import java.io.IOException; import javax.servlet.Filter; impo ...

  3. javascript 实现 对XML文件 2级/3级联动操作

    js代码 //实现对xml文档的读取:function loadXMLDoc(dname) { try // Internet Explorer { xmlDoc = new ActiveXObjec ...

  4. CISCO2960配置vlan

    一.VTP配置 1.VLAN database 2.(VLAN)#vtp domain wx 3.(VLAN)#vtp server 二.VLAN配置 1.VLAN database 2.(VLAN) ...

  5. 关于Calendar中设置月份比实际小1的问题

    有如下程序,转化两个字符串数字为date类型,并判断是历史上的星期几,是否同为星期一 代码如下: public static void main(String[] args) throws Parse ...

  6. JavaScript中的设计模式:单例模式

    定义 单例模式就是一个类只能实例化一个对象,并且提供了一个访问它的全局访问点. 一般在实现登陆框,或者一个全局的控制器都会用到单例模式.现实中有window对象,线程池,全局缓存等. 简单实现 var ...

  7. bzoj2120: 数颜色 [莫队][分块]

    Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜 ...

  8. (转)$.extend()方法和(function($){...})(jQuery)详解

    1.    JS中substring与substr的区别 之前在项目中用到substring方法,因为C#中也有字符串的截取方法Substring方法,当时也没有多想就误以为这两种方法的使用时一样的. ...

  9. 跨主机网络概述 - 每天5分钟玩转 Docker 容器技术(48)

    前面已经学习了 Docker 的几种网络方案:none.host.bridge 和 joined 容器,它们解决了单个 Docker Host 内容器通信的问题.本章的重点则是讨论跨主机容器间通信的方 ...

  10. 手把手教小白如何用css+js实现页面中图片放大展示效果

    1.前言      很多童鞋会在项目中遇到一些上传图片,展示图片的操作,但是图片呢有大有小,为了页面的美观,有时候我们需要将图片展示成固定宽高度,但是呢,领导就会说,我想看大图片,怎么办?想看就看呀, ...