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 ...
随机推荐
- 关于 OJ1575的参考题解
#include <stdio.h>int main(){ int a,b; scanf("%d",&a); b=0; while(a) { b+=a%10; ...
- python之socket
一.初识socket socket 是网络连接端点,每个socket都被绑定到一个特定的IP地址和端口.IP地址是一个由4个数组成的序列,这4个数均是范围 0~255中的值(例如, ...
- ACM 杭电HDU 2084 数塔 [解题报告]
数塔 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...
- Sublime Text的常用插件
工欲善其事,必先利其器.好的插件会更多的帮助我们装逼. 最新感悟:也不要装太多的插件.有些无用的插件可以删除,有时反而臃肿.bloger下载官方最新稳定版st3 3126下载下来25M左右.安装十来个 ...
- 常用js方法
function dateGetter(name, size, offset, trim) { offset = offset || 0; return function (date) { var v ...
- Asp.net管道模型(管线模型)
Asp.net管道模型(管线模型) 前言 为什么我会起这样的一个标题,其实我原本只想了解asp.net的管道模型而已,但在查看资料的时候遇到不明白的地方又横向地查阅了其他相关的资料,而收获比当初预 ...
- mac系统安装node
1.node 是通过brew来安装的,所以第一步先安装brew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebre ...
- HTML5 十大新特性(九)——Web Storage
H5的webStorage技术一共提供了两个对象:window.sessionStorage和window.localStorage. 一.window.sessionStorage--会话级存储 存 ...
- apache与nginx的虚拟域名配置
由于开发需求,项目有时候需要设置虚拟域名进行测试.下面是分别是apache和nginx的配置 一.apache 环境:wampserver2.5 1.修改host文件 C:\Windows\Syste ...
- Virtualbox安装USB2.0/3.0
系统:Ubuntu16.04 软件:Virtualbox5.1 1.打开Virtualbox,不启动虚拟系统. 2.点击设置->USB->启动usb2.0. 3.若发现不能启用,则到官网下 ...