co函数库源码解析

一、co函数是什么  

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

二、co函数怎么用

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

1 function* gen(){
2   var f1 = yield func1;
3   var f2 = yield fnuc2;
4    //sth to do
5 };

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

1 // 手动执行
2 var wp = gen()
3 gen.next()
4 gen.next()
5 // co 函数
6 var co = require(‘co‘)
7 co(gen)

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

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

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

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


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

四、co源码解析

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

4.1  辅助函数

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

 1 /**
 2  * obj 是否promise
 3  * 利用promise.then存在且为function
 4  */
 5
 6 function isPromise(obj) {
 7   return ‘function‘ == typeof obj.then;
 8 }
 9
10 /**
11  * obj是否Generator
12  * 利用Generator的next 和 throw 两属性为Fuction的特点加以判断
13  */
14
15 function isGenerator(obj) {
16   return ‘function‘ == typeof obj.next && ‘function‘ == typeof obj.throw;
17 }
18
19 /**
20  * 是否Generator方法
21  * 利用constructor的name和displayName属性。
22  * @example
23  * var a = {}
24  * a.constructor === Object
25  * a.constructor.name // "Object"
26  */
27
28 function isGeneratorFunction(obj) {
29   var constructor = obj.constructor;
30   if (!constructor) return false;
31   if (‘GeneratorFunction‘ === constructor.name || ‘GeneratorFunction‘ === constructor.displayName) return true;
32   return isGenerator(constructor.prototype);
33 }
34
35 /**
36  * 判断是否干净对象
37  * 利用constructor 属性。
38  * @example
39  * Object.constructor === Object
40  */
41
42 function isObject(val) {
43   return Object == val.constructor;
44 }

  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作为后面的执行的参数

 1 function onFulfilled(res) {
 2       var ret;
 3       try {
 4        /**
 5         * 执行next,获取执行结果
 6         */
 7         ret = gen.next(res);
 8       } catch (e) {
 9         return reject(e);
10       }
11       // 调用实现自动执行的关键函数,next
12       next(ret);
13       return null;
14     }

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

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

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

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

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

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

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

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

时间: 2024-10-13 23:49:18

co函数库源码解析的相关文章

iOS开源库源码解析之Mantle

来自Leo的原创博客,转载请著名出处 我的StackOverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage Mantle(本文) AFNetworking(3.0) MBProgressHud SwiftJSON MagicRecord Alamofire 前言 iOS开发中,不管是哪种设计模式,Model层都是不可或缺的.而Model层的第三方库常用的库有以下几个 JSONModel Mantle MJExtension JSON data到对象的转换原

iOS开源库源码解析之SDWebImage

来自Leo的原创博客,转载请著名出处 我的stackoverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage(本文) 前言 SDWebImage是iOS开发中十分流行的库,大多数的开发者在下载图片或者加载网络图片并且本地缓存的时候,都会用这个框架.这个框架相对来说,源代码还是比较少的.本文会详细的讲解这些类的架构关系和原理. 本文会先介绍类的整体架构关系,先有一个宏观的认识.然后讲解sd_setImageWithURL的加载逻辑,因为这是SDWebImage

Android常用库源码解析

图片加载框架比较 共同优点 都对多级缓存.线程池.缓存算法做了处理 自适应程度高,根据系统性能初始化缓存配置.系统信息变更后动态调整策略.比如根据 CPU 核数确定最大并发数,根据可用内存确定内存缓存大小,网络状态变化时调整最大并发数等. 支持多种数据源支持多种数据源,网络.本地.资源.Assets 等 不同点 Picasso所能实现的功能,Glide都能做,无非是所需的设置不同.但是Picasso体积比起Glide小太多. Glide 不仅是一个图片缓存,它支持 Gif.WebP.缩略图.Gl

iOS开源库源码解析之AsnycDispalyKit

来自Leo的原创博客,转载请著名出处 我的stackoverflow 前言 最近心血来潮,想研究下FaceBook的AsnycDispalyKit的源代码,学习一些界面优化的技术以及编码风格.这篇文章,会详细的记录下我认为对新手有用的部分.后面有空的时候,继续研究其他几个iOS开发很流行的库-AFNetworking,SDWebImage,MBProgressHud,Mantle等`.AsnycDisplayKit是一个非常庞大的库,所以我尽量捞干的讲. 关于AsyncDisplayKit 文档

二.jQuery源码解析之构建jQuery之构建函数jQuery的7种用法

一:$(selectorStr[,限制范围]),接受一个选择器(符合jQuery规范的字符串),返回一个jQuery对象;二:$(htmlStr[,文档对象]),$(html[,json对象])传入html字符串,创建一个新的dom元素 三:$(dom元素),$(dom元素集合)将dom元素转换成jQuery对象.四:$(自定义对象)封装普通对象为jQuery对象.五:$(回调函数)绑定ready事件监听函数,当Dom加载完成时执行.六:$(jQuery对象)接受一个jQuery对象,返回一个j

黄聪:WordPress动作钩子函数add_action()、do_action()源码解析

WordPress常用两种钩子,过滤钩子和动作钩子.过滤钩子相关函数及源码分析在上篇文章中完成,本篇主要分析动作钩子源码. 然而,在了解了动作钩子的源码后你会发现,动作钩子核心代码竟然跟过滤钩子差不多!是的,至此,我不得不告诉你,动作钩子只是WP开发者为了区分概念而把过滤钩子另外命名的一种东西!当然,它们还是有一些细微的差别,下面我们将从源码来深入解读. 动作钩子概念:动作钩子是WP代码执行到某处或某个事件发生时触发的一系列函数,插件可以利用动作钩子API在WP代码执行的特定点之前插入一系列函数

jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解

异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct)和jQuery.when().本节讲解Callbacks,也就是回调函数列表 回调函数用于管理一组回调函数,支持添加.移除.触发.锁定和禁用回调函数,为jQuery.ajax.jQuery.Deferred()和ready()事件提供基础功能,我们也可以基于它编写新的组件. 使用方法:$.Callb

Android 热修复Nuwa的原理及Gradle插件源码解析

现在,热修复的具体实现方案开源的也有很多,原理也大同小异,本篇文章以Nuwa为例,深入剖析. Nuwa的github地址 https://github.com/jasonross/Nuwa 以及用于hotpatch生成的gradle插件地址 https://github.com/jasonross/NuwaGradle 而Nuwa的具体实现是根据QQ空间的热修复方案来实现的.安卓App热补丁动态修复技术介绍.在阅读本篇文章之前,请先阅读该文章. 从QQ空间终端开发团队的文章中可以总结出要进行热更

jquery源码解析:代码结构分析

本系列是针对jquery2.0.3版本进行的讲解.此版本不支持IE8及以下版本. (function(){ (21, 94)     定义了一些变量和函数,   jQuery = function(){}; (96,283)   给jQuery对象添加一些属性和方法(实例方法,通过$("div")这类的jQuery实例对象来调用) (285,347)   extend : jQuery的继承方法 (349,817)   jQuery.extend():扩展一些工具方法(静态方法,直接通