# 神奇的co
* [co是什么?](https://i5ting.github.io/wechat-dev-with-nodejs/async/co.html#co是什么)
* [源码解析](https://i5ting.github.io/wechat-dev-with-nodejs/async/co.html#源码解析)
* [co引出的“血案”](https://i5ting.github.io/wechat-dev-with-nodejs/async/co.html#co引出的血案)
## co是什么?
[https://github.com/tj/co](https://github.com/tj/co)
> 官方说:The ultimate generator based flow-control goodness for nodejs (supports thunks, promises, etc)
我的解释
* 是generator的执行器
* 可是使用generator里的Yieldable支持的所有形式(supports thunks, promises, etc)
* 返回的结果是promise
那么它的用途就很明显了
* 所有出现generator的地方它都可以出现
* 在以后的async/await里await后面可以接promise
## 源码解析
co@4.6版本不到240行代码,整体来说,还算比较简单。但并不容易阅读
~~~
// 核心代码
function co(gen) {
// 缓存this
var ctx = this;
var args = slice.call(arguments, 1)
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
// 重点,co的返回值是Promise对象。为什么可以then和catch的根源
return new Promise(function(resolve, reject) {
// 如果你懂Promise规范,就知道这是解决状态回调,这是首次调用
onFulfilled();
/**
* @param {Mixed} res
* @return {Promise}
* @api private
*/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* @param {Error} err
* @return {Promise}
* @api private
*/
// 如果你懂Promise规范,就知道这是拒绝状态回调
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
// generator执行器
// 如果ret.done,返回ret.value
// 否则,
function next(ret) {
// 如果执行完成,直接调用resolve把promise置为成功状态
if (ret.done) return resolve(ret.value);
// 把yield的值转换成promise
// 支持 promise,generator,generatorFunction,array,object
// toPromise的实现可以先不管,只要知道是转换成promise就行了
var value = toPromise.call(ctx, ret.value);
// 成功转换就可以直接给新的promise添加onFulfilled, onRejected。当新的promise状态变成结束态(成功或失败)。就会调用对应的回调。整个next链路就执行下去了。
// 为什么generator可以无限的next下去呢?
// return value.then(onFulfilled, onRejected);意味着,又要执行onFulfilled了
// onFulfilled里调用next(ret);
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) + '"'));
}
});
}
~~~
读此源码要点
* 必须深刻理解Promise实现,知道构造函数里的onFulfilled和onRejected是什么意思
* 必须了解generator的执行机制,理解迭代器里的next以及next的返回对象{value:'',done: true}
核心代码入口是onFulfilled,无论如何第一次的next(ret)是一定要执行的,因为generator必须要next()一下的。
所以next(ret)一定是重点,而且我们看onFulfilled和onRejected里都调用它,也就是所有的逻辑都会丢在这个next(ret)方法里。它实际上是一个状态机的简单实现。
~~~
// generator执行器
// 如果ret.done,返回ret.value
// 否则,
function next(ret) {
// 如果执行完成,直接调用resolve把promise置为成功状态
if (ret.done) return resolve(ret.value);
// 把yield的值转换成promise
// 支持 promise,generator,generatorFunction,array,object
// toPromise的实现可以先不管,只要知道是转换成promise就行了
var value = toPromise.call(ctx, ret.value);
// 成功转换就可以直接给新的promise添加onFulfilled, onRejected。当新的promise状态变成结束态(成功或失败)。就会调用对应的回调。整个next链路就执行下去了。
// 为什么generator可以无限的next下去呢?
// return value.then(onFulfilled, onRejected);意味着,又要执行onFulfilled了
// onFulfilled里调用next(ret);
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) + '"'));
}
~~~
情景1: 状态完成
~~~
// 如果执行完成,直接调用resolve把promise置为成功状态
if (ret.done) return resolve(ret.value);
~~~
情景2: next,跳回onFulfilled,递归
~~~
// 成功转换就可以直接给新的promise添加onFulfilled, onRejected。当新的promise状态变成结束态(成功或失败)。就会调用对应的回调。整个next链路就执行下去了。
// 为什么generator可以无限的next下去呢?
// return value.then(onFulfilled, onRejected);意味着,又要执行onFulfilled了
// onFulfilled里调用next(ret);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
~~~
情景3: 捕获异常
~~~
// 如果以上情况都没发生,报错
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
~~~
以上是核心代码说明。之前我们讲了co实际有2种api,有参数和无参数的,很明显以上是无参数的generator执行器,那么有参数的wrap呢?
~~~
// 为有参数的generator调用,提供简单包装
co.wrap = function (fn) {
createPromise.__generatorFunction__ = fn;
return createPromise;
function createPromise() {
// 重点,把arguments给fn当参数。
// call和apply是常规js api
return co.call(this, fn.apply(this, arguments));
}
};
~~~
通过call和apply组合使用,知识点比较简单,但这样用还是挺巧妙的。
其他的就基本是工具类了,其实也挺有意思的,自己看吧
## co引出的“血案”
ES6的Generator本意是为了计算而设计的迭代器,但tj觉得它可以用于流程控制,于是就有了co,co的历史可以说经历了目前所有的流程控制方案,而且由于支持Generator和yield就导致yieldable。
实际上co和Generator是把双刃剑,给了我们强大便利的同时,也增加了非常多的概念,可能是过渡性的,也可能是过时的。
可是,你真的需要了解这么多么?从学习的角度,当然是多多意义,如果从实用的角度看,你可能不需要。
存在即合理,那么我们就看看这“血案”吧:
* 学习ES6的Generator
* 了解ES6的迭代器和迭代器相关的2种协议,了解for-of
* 了解co和co的2种用法,源码
* 了解yieldable 5种(包括不常用Thunk)
* 如果是koa,还需要了解convert和compose
- 前言
- 1 skill
- 1.1 Coding WebIDE
- 1.2 git
- 1.3 extra practice
- 1.4 预习
- 2 nodejs入门
- 2.1 入门
- 2.2 安装
- 2.3 helloworld
- 2.4 commonJS规范
- 2.5 模块导出
- 2.6 Nodejs代码调试
- 2.7 编写Nodejs模块
- 2.8 最小化问题
- 2.9 随堂练习
- 3 异步流程控制
- 3.1 什么时候会用到异步流程控制
- 3.2 简单做法async模块
- 3.3 Promise/a+规范
- 3.4 Node.js Promise/a+实现
- 3.5 生成器Generators/yield
- 3.6 Async函数/Await
- 3.7 神奇的co
- 3.8 5种 yieldable
- 3.9 学习重点
- 3.10 随堂练习
- 4 express和微信开发入门
- 4.1 入门
- 4.2 connect
- 4.3 静态Http服务器
- 4.4 那些预处理器
- 4.5 路由
- 4.6 视图与模块引擎
- 4.7 中间件
- 4.8 更多实践
- 4.9 微信入门
- 4.10 随堂练习:完成登录、注册功能
- 5 微信实例与H5实践
- 5.1 微信基础和sandbox
- 5.2 公众号菜单和自动回复
- 5.3 微信OAuth用户授权
- 5.4 微信分享
- 5.5 wechat-api
- 5.6 H5-上篇
- 5.7 H5-下篇
- 5.8 随堂练习
- 6 weui实战
- 6.1 使用bower
- 6.2 移动端抽象
- 6.3 优化滑动列表
- 6.4 weui
- 6.5 让weui和iscroll结婚
- 6.6 优化事件
- 6.7 how-to-write-h5
- 6.8 优化无止境
- 6.9 随堂练习
- 7 微信支付
- 7.1 吹个牛
- 7.2 支付概述
- 7.3 科普几个概念
- 7.4 准备
- 7.5 调试
- 7.6 公众号支付(JSAPI)
- 7.7 对账单
- 7.8 数据处理
- 7.9 随堂练习
- 8 项目实战《付费课程系统MVP》
- 8.1 需求分析
- 8.2 ui/ue
- 8.3 技术栈
- 8.4 模型
- 8.5 静态api
- 8.6 开发
- 8.7 部署
- 8.8 监控
- 8.9 数据统计
- 8.10 demo
- 9 高级篇
- 9.1 前后端分离实践?
- 9.2 如何展望未来的大前端
- 9.3 容器和微服务
- 10 答疑问题收集