koa框架异步返回值的操作(co,koa-compose)
最近在做demo的时候使用了koa框架,自己做了一个静态服务器,首先判断访问文件是否存在,在回调函数中设置了this.body,run之后,各种404,花了N长的时间把koa-compose和co模块看了下,只能说自己终于有了一个比较浅显的认识了。
首先我们看下koa-compose的代码,就短短的十几行。
function compose(middleware){
return function *(next){
var i = middleware.length;
var prev = next || noop();
var curr;
while (i--) {
curr = middleware[i];
prev = curr.call(this, prev);
}
yield *prev;
}
}
function *noop(){}
在koa框架中,中间件都是generator函数,而koa-compose的作用就是将generator函数关联起来。
如代码所示,该函数返回一个generator函数,在返回的generator函数中,从最后一个中间件开始,prev最初赋值为一个空的generator函数。在while函数中,curr赋值为最后一个generator函数,然后用koa的ctx去调用最后一个generator函数,并将最初的prev即一个空的generator函数作为参数传入(最后一个中间件一般都没有yield,所以不做过多讨论),返回一个generator对象,并将其赋值给prev,接着调用倒数第二个中间件,并将prev当做参数传入到中间件的next参数中,然后将倒数第二个中间件的generator对象赋值给prev。分析下此时的prev对象,此时prev的next函数执行的是倒数第二个中间件的代码并返回倒数第一个中间件的generator对象(此时讨论的是generator函数只有一个yield next)。一直while后,知道prev为第一个中间件的generator对象,并用yield *prev返回。
接着看co的几个核心代码
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
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) + '"'));
}
});
}
function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
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;
}
当co函数拿到koa-comprose返回的generator函数(或者对象)后,会return一个promise,在promise的function中
调用了onFulfilled函数。在该函数中,会调用gen的next方法,这样会执行第一个中间件yield和之前的的代码,并返回yield 的Object(不特指一个new Object),并赋值给ret, 然后调用next方法,并将ret对象传入。如果第一个generator迭代完成(即你的gen中没有yield),再调用没有yield的gen所在的promise的reslove方法,否则用ctx调用toPromise方法,并将第二个中间件的generator对象放入参数位置。在toPromise方法中,如果放入的是一个undefined,则直接返回undefined,否则将其放入isPromise方法中,让我们看看isPromise方法
function isPromise(obj) {
return 'function' == typeof obj.then;
}
只有简单的判断对象是否有then方法,如果有的话,在toPromise中直接返回obj,否则的话,判断obj是generator函数还是generator对象,如果是的话,则重新调用co函数,并将obj当做参数传入,假设现在只有两个中间件。然后第二个中间件的generator对象,调用next并得到一个ret对象,并用该ret对象,将其作为参数传入next中,因为最后一个中间件没有yield next,所以第二个中间件generator所在的promise对象调用resolve方法,至此第二个中间件执行完毕,并将第二个中间件所在的promise通过 if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj); 返回第一个中间件的next方法中,并将其赋值给value。 然后给该promise添加then中的逻辑,此时开始执行第一个中间件剩余部分代码.如果第二个中间件(then中得到的值为迭代方法return的值)。
function co(gen) {
var ctx = this;
var args = slice.call(arguments, )
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
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
*/
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* Get the next value in the generator,
* return a promise.
*
* @param {Object} ret
* @return {Promise}
* @api private
*/
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
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) + '"'));
}
});
}
/**
* Convert a `yield`ed value into a promise.
*
* @param {Mixed} obj
* @return {Promise}
* @api private
*/
function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
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;
}
在上面我们只讨论了中间件之间的yield,如果在yield next中间加入了yeild function * b(){}或者yeild new Promise该如何?
还是以两个中间件为例,在第二个中间件yield Promise
对于koa-compose没有太大变化
在co中,第一个中间件的执行没有变化,在第二次调用co并把第二个中间件的迭代对象放入的时候,当执行next方法的时候,会将一个value为promise的对象传入到next方法中,在toPromise中,因为obj本身为一个promise,会直接返回promise避免了再一次调用co。此时co执行第二次中,next方法中的value为该promise,然后给该promise添加then方法,待该Promise调用了reslove后,在该then方法中,再一次开始第二个中间件剩下代码的执行。所以在koa框架中如果想要异步设置this.body,需要yield Promise(function(re,rej){})并在function中设置this.body,当异步执行完成后,调用re方法,传入自己想要传递的值,koa框架继续执行剩余代码。
第一次写这么长的,感觉有点渣...
koa框架异步返回值的操作(co,koa-compose)的更多相关文章
- int不可为null引发的 MyBatis做持久层框架,返回值类型要为Integer问题
MyBatis做持久层框架,返回值类型要为Integer MyBatis 做持久层时,之前没注意,有时候为了偷懒使用了int类型做为返回的类型,这样是不可取的,MyBatis做持久层框架,返回值类型要 ...
- 前端框架 json 返回值
layui: string strJson = "{\"code\": \"0\",\"msg\": \"\" ...
- laravel框架总结(十) -- 返回值
以前用CI框架对于返回值没有过多关注,但是发现使用laravel框架的时候出现了一些小问题,特意实践总结了一些常用情形,希望对大家有所帮助 先理解几个概念: 1>StdClass 对象=&g ...
- 从头实现一个koa框架
koajs是最流行的nodejs后端框架之一,有很多网站都使用koa进行开发,同时社区也涌现出了一大批基于koa封装的企业级框架.然而,在这些亮眼的成绩背后,作为核心引擎的koa代码库本身,却非常的精 ...
- springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)
之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...
- .net 表达式返回值和等号赋值的区别
.net 7.0的新特性中,有一个使用表达式体返回值的操作.请看如下代码: private string _userName=""; public string UserName{ ...
- 深入PHP内核之函数和返回值
1.关于返回值,PHP内核中使用了大量的宏来实现,我们先看一个函数 PHP_FUNCTION 宏的定义(Zend/zend_API.h) #define PHP_FUNCTION ZEND_FUNC ...
- 关于ExecuteNonQuery执行的返回值(SQL语句、存储过程)
因为msdn中说返回受影响的行数: Executes a Transact-SQL statement against the connection and returns the number of ...
- 关于ExecuteNonQuery执行存储过程的返回值 、、实例讲解存储过程的返回值与传出参数、、、C#获取存储过程的 Return返回值和Output输出参数值
关于ExecuteNonQuery执行存储过程的返回值 用到过ExecuteNonQuery()函数的朋友们在开发的时候肯定这么用过. if(cmd.ExecuteNonQuery("xxx ...
随机推荐
- linux开发摘要
1.linux内核文档链接点击打开链接 2.配置文件 在out\target\product\project\obj\KERNEL_OBJ\.config中可以看到 # CONFIG_MTD_LPDD ...
- javascript面向对象(学习和理解)
js中创建变量基本如下: var name = 'saodiseng'; var email = 'wuyucoder@126.com'; var website = 'http://www.cnbl ...
- Swift根据日期字符串返回日期是星期几
最近在做的一个IOS项目中需要根据日期得出日期代表的是星期几,日期以字符串的形式获得,于是该方法可以简单描述如下: /* * 根据日期格式字符串返回日期代表星期几 * 参数:dateTime,字符串类 ...
- Knockout 官网翻译
Knockout 新版应用开发教程之创建view models与监控属性 章节导航 最近抽出点时间研究MVVM,包括司徒正美的avalon,google的angular,以及Knockout,博客园T ...
- javascript学习内容--改变样式、取消设置、显示内容、隐藏内容
<head> <style> body{font-size:12px;} #txt{ height:400px; width:600px; border:#333 solid ...
- mac命令学习记录
1. 查找程序运行路径: which xxxx 2. 查找文件安装路径: whereis xxxx; 3. 编辑配置文件:vi ./.xxx : 进入需要编辑的文件: i 进行编辑 :输入:wq ...
- mysql--sqlalchemy.exc.IntegrityError: (IntegrityError) (1215, 'Cannot add foreign key constraint'
今天在使用mysql时遇到的问题,最后发现问题是,数据类型与外键数据类型不同,改正过来就没有问题了.
- JAVA GUI布局管理器
边界布局管理器: a.布局方式:是把整个容器划分为五个部分.东西南北中,南北要贯通,中间最大 (不仅是中间的范围最大,权利也最大)当周边不存在的时候中间会占领周边,当中间不存在的时候周边不能占据中间 ...
- WordPress网站搭建
. 1.进入 var/www/html中放入里的文件 2.. 安装http php php-sql [root@jw38 yum.repos.d]# systemctl restart httpd.s ...
- 安装MySQL(简便)
1.在本地虚拟机上上传mysql的5个安装包 2.查看opt目录下是否有这5个安装包 yum install /var/opt/mysql-community-* -y //安装MySQL syste ...