Generator库co4.6使用及源码分析
原文链接 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入门
Generator库co4.6使用及源码分析的更多相关文章
- Koa源码分析(一) -- generator
Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: 1. Koa源码分析(一) -- generator 2. Koa源码分析(二) -- co的实现 ...
- [转]数据库中间件 MyCAT源码分析——跨库两表Join
1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...
- 比特币源码分析--C++11和boost库的应用
比特币源码分析--C++11和boost库的应用 我们先停下探索比特币源码的步伐,来分析一下C++11和boost库在比特币源码中的应用.比特币是一个纯C++编写的项目,用到了C++11和bo ...
- Hadoop2源码分析-YARN 的服务库和事件库
1.概述 在<Hadoop2源码分析-YARN RPC 示例介绍>一文当中,给大家介绍了YARN 的 RPC 机制,以及相关代码的演示,今天我们继续去学习 YARN 的服务库和事件库,分享 ...
- 从Generator入手读懂co模块源码
这篇文章是讲JS异步原理和实现方式的第四篇文章,前面三篇是: setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop 从发布订阅模式入手读懂Node.js的E ...
- 从源码分析node-gyp指定node库文件下载地址
当我们安装node的C/C++原生模块时,涉及到使用node-gyp对C/C++原生模块的编译工作(configure.build).这个过程,需要nodejs的头文件以及静态库参与(后续称库文件)对 ...
- Redis网络库源码分析(1)之介绍篇
一.前言 Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析.其实网上已经有非常多有关这个网络库 ...
- Koa源码分析(二) -- co的实现
Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...
- 异步编程之co——源码分析
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
随机推荐
- 工作中那些提高你效率的神器(第一篇)_Everything
引言 无论是工作还是科研,我们都希望工作既快又好,然而大多数时候却迷失在繁杂的重复劳动中,久久无法摆脱繁杂的事情. 你是不是曾有这样一种想法:如果我有哆啦A梦的口袋,只要拿出神奇道具就可解当下棘手的问 ...
- jQuery中事件与动画的总结
1.加载DOM 1.1.window事件 window.onload=function(){}.... 时机:其他资源都加载完毕后,再执行 $(function(){}) ……:只是 ...
- Unity3D UGUI之DoTweenAnimation脚本控制动画方法
DOTweenAnimation脚本: Loops循环:-1时即永久循环播放. Loops Type 是选择播放模式. Ease属性里有很多,暂时只知道Linear的效果,其他有待单独写. ID下面可 ...
- Java的书写格式,标识符及命名规则,注释
Java的书写格式,标识符及命名规则,注释 1.Java语言的书写格式(约定成俗) 1) 大括号要对齐(左大括号与句尾对其,后面大括号与句头对齐),并且成对写 2) 左大括号前面有空格 3) 遇到左大 ...
- 【五】将博客从jekyll迁移到了hexo
本系列有五篇:分别是 [一]Ubuntu14.04+Jekyll+Github Pages搭建静态博客:主要是安装方面 [二]jekyll 的使用 :主要是jekyll的配置 [三]Markdo ...
- 通过html和css做出下拉导航栏的效果
通过观察了百度的首页,对于更多产品一栏,觉得可以不涉及JS便可写出下拉导航栏的效果 1.先设计出大体的框架 <div class="nav"> <ul> & ...
- Visual Studio “14” CTP 4
微软发布于10月6日发布了Visual Studio "14"CTP 4,本次发布的更新主要包括:ASP.NET vNext runtime和一些工具的优化(ASP.NET vNe ...
- 喜大普奔,微软Microsoft JDBC Driver For SQL Server已发布到maven中央仓库
相信通过java和SQLServer开发应用的同学们都经历过如下类似的问题. 微软提供的JDBC官方驱动没有放置在Maven仓库中,这样如果你的Java应用需要访问SQL Server,你不得不下载s ...
- 七天学会ASP.NET MVC (四)——用户授权认证问题
小编应各位的要求,快马加鞭,马不停蹄的终于:七天学会 Asp.Net MVC 第四篇出炉,在第四天的学习中,我们主要了学习如何在MVC中如何实现认证授权等问题,本节主要讲了验证错误时的错误值,客户端验 ...
- C# 服务程序 - 调试服务
前言:本篇文章环境是VS2015,win10.如果有任何的差别,请注意 1. 创建服务程序 1)用VC创建服务程序,叫做 MyTestWindowsService 创建完成之后,可以看到 2)添加安装 ...