原文链接 http://www.cnblogs.com/ytu2010dt/p/6043947.html

co4.x已经抛弃了原来thunk转而结合promise实现。

一:promise

promsie是es6原声支持的把一步嵌套转换为线型调用的一种方式,angular的$q和node的q包都是基于promise A+规范封装的 。原生使用方法如下

//首先对异步操作封装
function readFile(path) {
return new Promise(function(resolve, reject) {
fs.readFile(path, 'utf8', function(err, data) {
if (!err) {
resolve(data);
} else {
reject(err);
}
})
});
}
// 调用如下(没次调用完返回一个新的promise,在最后的catch里处理错误)

readFile('01.txt')
    .then(function(data) {
if (data) {
console.log(data)
return readFile('02.txt')
}
})
.then(function(data) {
if (data) {
console.log(data)
return readFile('03.txt')
}
})
.catch(function(err) {
console.log(err)
})

二:Generator

function* fn(a){
var x=yield a;
//x==4
var y=yield x;
var z=yield y+1;
}
var demo=fn(1)
console.log(demo.next(2))//{ value: 1, done: false }
console.log(demo.next(3))//{ value: 3, done: false }
console.log(demo.next(4))//{ value: 5, done: false }
console.log(demo.next(5))//{ value: undefined, done: true }

generator 函数和普通函数不通之处在与function后面需要加一个*,直接调用时不会执行,必须调用next方法才会执行。而且函数内部可以有多个yield即可以返回多个值。有点类似打断点,调用一次next方法执行一步。

yield默认没有返回值,可以在调用next时加入参数作为上一个yield的返回值

上面的例子给fn函数传入1,此时不执行,

调用第一个next方法,此时a==1,此时无返回值,value==1

调用第二个next方法传入3,即x==3,value==3

调用第三个next传入4 即y==4  value==4+1==5

再次调用next方法 generator执行完毕,所以value为undefined done为true

三:co

co是express和koa的作者tj大神基于es6 Generator和promise实现的一个解决js异步回调嵌套地狱的库除了注视100行左右代码,可谓短小精悍。

co允许我们像写同步代码一样鞋异步代码,并且可以用try catch捕获异常。调用co始终返回一个promise所以也可以用then catch。 使用方法如下(传入一个generator function)

注:function* (){}()是generator

function* (){}是generator function

co(function*() {
try {
var a = readFile('01.txt');
var b = readFile('02.txt');
var c = readFile('03.txt');
//yield后面可以跟对象、数组、promise、Thunks、Generators、Generator Functions
var res = yield [a, b, c];
return res;
} catch (err) {
console.error(err.message); // "boom"
}
})
.then(function(data) {
console.log(data)
})
.catch(function(error) {
console.log(error)
});

四:co源码

1.调用co始终返回一个promise,这也是co后面也可以跟then catch的原因

2.co的核心就是通过next方法不断调用 generator的next方法,直到generator执行完毕。

3.yield 后面个跟以下几种function, promise, generator, array, or object,然后调用toPromise方法将以上几种数据格式都转化为promise

核心代码如下

4.可以通过co.wrap把一个generator函数转化为promise,原理是co.wrap内部调用了co函数,因为co始终返回一个promise,所以co.wrap可以将generator函数转化为promise。

另外如果需要调用两个除了参数不同其余都相同的co,此时可以用co.wrap()返回普通函数带入实参调用。如下

var getFile = co.wrap(function* (file){
var filename = yield readFile(file, 'utf-8');
return filename;
});
getFile('./01.txt')
.then(function(data){
console.log(data)
})
getFile('./02.txt')
.then(function(data){
console.log(data)
})

5.co函数如下

function co(gen) {
//这里的this在node中指向global
var ctx = this;
var args = slice.call(arguments, 1)
//返回一个promise对象
return new Promise(function(resolve, reject) {
//如果gen为一个generator function则执行函数返回一个generator对象
//相当于执行function* (){}()
if (typeof gen === 'function') gen = gen.apply(ctx, args);
//如果gen为空或者不是generator function则直接把gen放在promise的resolve中作为参数
if (!gen || typeof gen.next !== 'function') return resolve(gen);
//默认调用一次onFulfilled
onFulfilled();
function onFulfilled(res) {
console.log(res)
var ret;
try {
//第一次调用next不传参数
ret = gen.next(res);
} catch (e) {
return reject(e);
}
//递归yeild
next(ret);
}
//如果yeild后面跟的不是function, promise, generator, array, or object
//的一种,则执行这里。
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
console.log(ret)
//如果generator function执行完毕,直接返回数据(undefined)
if (ret.done) return resolve(ret.value);
//generator function没有执行完毕,把所有ret.value转化为promise
var value = toPromise.call(ctx, ret.value);
//如果转换为promise成功则执行promise
//yeild后面只能跟function, promise, generator, array, or object
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) + '"'));
}
});
}

其余就是一系列的数据结构抓换为ptomise

//数组转化为promise,对数组用map遍历,然后把每一项转换为promise。
//promise的all可以传入多个promise,并返回一个新的ptomise
//所有子promise执行完毕后,这个promise才进入resolve
function arrayToPromise(obj) {
return Promise.all(obj.map(toPromise, this));
}
//对象转换为promise
function objectToPromise(obj){
//新建一个空obj
var results = new obj.constructor();
//返回一个obj的键数组
var keys = Object.keys(obj);
//这个空数组用于存放对象中所有值转换完的promise
var promises = [];
//循环便利每一项,调用toPromise
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var promise = toPromise.call(this, obj[key]);
//如果转换成为promise则push到promises
if (promise && isPromise(promise)) defer(promise, key);
else results[key] = obj[key];
}
//Promise.all执行所有的promise然后返回一个新的promise
return Promise.all(promises).then(function () {
return results;
}); function defer(promise, key) {
results[key] = undefined;
promises.push(promise.then(function (res) {
results[key] = res;
}));
}
}

目前理解还不是特别深刻,以后有了新的理解随时补上。

参考文章  (1)阮老师es6入门

(2)搞定 koa 之 co源码

(3)co-4.0新变化

Generator库co4.6使用及源码分析的更多相关文章

  1. Koa源码分析(一) -- generator

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: 1. Koa源码分析(一) -- generator 2. Koa源码分析(二) -- co的实现 ...

  2. [转]数据库中间件 MyCAT源码分析——跨库两表Join

    1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...

  3. 比特币源码分析--C++11和boost库的应用

    比特币源码分析--C++11和boost库的应用     我们先停下探索比特币源码的步伐,来分析一下C++11和boost库在比特币源码中的应用.比特币是一个纯C++编写的项目,用到了C++11和bo ...

  4. Hadoop2源码分析-YARN 的服务库和事件库

    1.概述 在<Hadoop2源码分析-YARN RPC 示例介绍>一文当中,给大家介绍了YARN 的 RPC 机制,以及相关代码的演示,今天我们继续去学习 YARN 的服务库和事件库,分享 ...

  5. 从Generator入手读懂co模块源码

    这篇文章是讲JS异步原理和实现方式的第四篇文章,前面三篇是: setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop 从发布订阅模式入手读懂Node.js的E ...

  6. 从源码分析node-gyp指定node库文件下载地址

    当我们安装node的C/C++原生模块时,涉及到使用node-gyp对C/C++原生模块的编译工作(configure.build).这个过程,需要nodejs的头文件以及静态库参与(后续称库文件)对 ...

  7. Redis网络库源码分析(1)之介绍篇

    一.前言 Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析.其实网上已经有非常多有关这个网络库 ...

  8. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  9. 异步编程之co——源码分析

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

随机推荐

  1. maven内部运行原理解析

    maven至今还是Java编程语言构建的事实标准,大部分项目还在使用maven来进行构建,因此了解maven内部运行的原理对定位和分析问题还是很有裨益的.本篇文章主要介绍一些maven内部运行过程中的 ...

  2. Zend Studio XDebug调试配置

    最近在配置zend studio时找了些资料,发现了这个,说的比较详细 搭建Zend Studio 10.5 和XDebug 环境,试图进行 Drupal的调试, 经历了一些困难,但是最终解决了问题, ...

  3. SQL Server 数据变更时间戳(timestamp)在复制中的运用

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 方案(Solution) 方案一(Solution One) 方案二(Solution Two ...

  4. 一步一步学ROP之linux_x64篇

    一步一步学ROP之linux_x64篇 一.序 **ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防 ...

  5. 用rem实现WebApp自适应的优劣分析

    关于rem实现屏幕自适应布局的讨论还是比较多的,刚好我也看到使用rem实现自适应的web app,所以也来凑下热闹. 说起rem,免不了要联系到em.px,这里简单提提他们的定义和特点. 1. px: ...

  6. 图片拾取器-PicPicker

    最近报名参加了360前端星计划,想当一名前端实习生,学习更多更流行的前端知识.然后需要完成一个作业,才能进培训,进了培训还得看运气才能留下,流程不少.书归正传,请看: 课后作业题目 请从下面两个题目中 ...

  7. iOS-App发布证书的申请与使用

    i开发环境:xcode5.1.1 iphonesdk:7.1 开发机器:iMac 真机部署测试:apple个人开发者ID 向导: 必备IDP证书和distribution证书(第一个证书是真机部署测试 ...

  8. 修改HTML5 input placeholder 颜色及修改失效的解决办法

    input::input-placeholder{color: #bdbdbd ;} /* 有些资料显示需要写,有些显示不需要,但是在编辑器webstorm中该属性不被识别 */ ::-webkit- ...

  9. Java基础-多线程编程-1.随便选择两个城市作为预选旅游目标。实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市。分别用Runnable接口和Thread类实现。

    1.随便选择两个城市作为预选旅游目标.实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000ms以内),哪个先显示完毕,就决定去哪个城市.分别用Runnable接口和Thread ...

  10. 实战Hybird app:内存溢出与优化

    pheongap项目:http://www.nduoa.com/apk/detail/646816 主要的问题: heap过大,内存低性能差的机子上引起奔溃,直接退出 关于web app的优化,不仅仅 ...