最近在做demo的时候使用了koa框架,自己做了一个静态服务器,首先判断访问文件是否存在,在回调函数中设置了this.body,run之后,各种404,花了N长的时间把koa-compose和co模块看了下,只能说自己终于有了一个比较浅显的认识了。

首先我们看下koa-compose的代码,就短短的十几行。

  1. function compose(middleware){
  2. return function *(next){
  3. var i = middleware.length;
  4. var prev = next || noop();
  5. var curr;
  6.  
  7. while (i--) {
  8. curr = middleware[i];
  9. prev = curr.call(this, prev);
  10. }
  11.  
  12. yield *prev;
  13. }
  14. }
  15.  
  16. 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的几个核心代码

  1. function co(gen) {
  2. var ctx = this;
  3. var args = slice.call(arguments, 1)
  4.  
  5. return new Promise(function(resolve, reject) {
  6. if (typeof gen === 'function') gen = gen.apply(ctx, args);
  7. if (!gen || typeof gen.next !== 'function') return resolve(gen);
  8.  
  9. onFulfilled();
  10.  
  11. function onFulfilled(res) {
  12. var ret;
  13. try {
  14. ret = gen.next(res);
  15. } catch (e) {
  16. return reject(e);
  17. }
  18. next(ret);
  19. }
  20.  
  21. function onRejected(err) {
  22. var ret;
  23. try {
  24. ret = gen.throw(err);
  25. } catch (e) {
  26. return reject(e);
  27. }
  28. next(ret);
  29. }
  30.  
  31. function next(ret) {
  32. if (ret.done) return resolve(ret.value);
  33. var value = toPromise.call(ctx, ret.value);
  34. if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
  35. return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
  36. + 'but the following object was passed: "' + String(ret.value) + '"'));
  37. }
  38. });
  39. }
  40.  
  41. function toPromise(obj) {
  42. if (!obj) return obj;
  43. if (isPromise(obj)) return obj;
  44. if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  45. if ('function' == typeof obj) return thunkToPromise.call(this, obj);
  46. if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  47. if (isObject(obj)) return objectToPromise.call(this, obj);
  48. return obj;
  49. }

当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方法

  1. function isPromise(obj) {
  2. return 'function' == typeof obj.then;
  3. }

只有简单的判断对象是否有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的值)。

  1. function co(gen) {
    var ctx = this;
    var args = slice.call(arguments, )
  2.  
  3. // 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);
  4.  
  5. onFulfilled();
  6.  
  7. /**
    * @param {Mixed} res
    * @return {Promise}
    * @api private
    */
  8.  
  9. function onFulfilled(res) {
    var ret;
    try {
    ret = gen.next(res);
    } catch (e) {
    return reject(e);
    }
    next(ret);
    }
  10.  
  11. /**
    * @param {Error} err
    * @return {Promise}
    * @api private
    */
  12.  
  13. function onRejected(err) {
    var ret;
    try {
    ret = gen.throw(err);
    } catch (e) {
    return reject(e);
    }
    next(ret);
    }
  14.  
  15. /**
    * Get the next value in the generator,
    * return a promise.
    *
    * @param {Object} ret
    * @return {Promise}
    * @api private
    */
  16.  
  17. 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) + '"'));
    }
    });
    }
  18.  
  19. /**
    * Convert a `yield`ed value into a promise.
    *
    * @param {Mixed} obj
    * @return {Promise}
    * @api private
    */
  20.  
  21. 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;
    }
  22.  
  23. 在上面我们只讨论了中间件之间的yield,如果在yield next中间加入了yeild function * b(){}或者yeild new Promise该如何?
  24.  
  25. 还是以两个中间件为例,在第二个中间件yield Promise
    对于koa-compose没有太大变化
    co中,第一个中间件的执行没有变化,在第二次调用co并把第二个中间件的迭代对象放入的时候,当执行next方法的时候,会将一个valuepromise的对象传入到next方法中,在toPromise中,因为obj本身为一个promise,会直接返回promise避免了再一次调用co。此时co执行第二次中,next方法中的value为该promise,然后给该promise添加then方法,待该Promise调用了reslove后,在该then方法中,再一次开始第二个中间件剩下代码的执行。所以在koa框架中如果想要异步设置this.body,需要yield Promisefunction(re,rej){})并在function中设置this.body,当异步执行完成后,调用re方法,传入自己想要传递的值,koa框架继续执行剩余代码。
  26.  
  27. 第一次写这么长的,感觉有点渣...

koa框架异步返回值的操作(co,koa-compose)的更多相关文章

  1. int不可为null引发的 MyBatis做持久层框架,返回值类型要为Integer问题

    MyBatis做持久层框架,返回值类型要为Integer MyBatis 做持久层时,之前没注意,有时候为了偷懒使用了int类型做为返回的类型,这样是不可取的,MyBatis做持久层框架,返回值类型要 ...

  2. 前端框架 json 返回值

    layui: string strJson = "{\"code\": \"0\",\"msg\": \"\" ...

  3. laravel框架总结(十) -- 返回值

    以前用CI框架对于返回值没有过多关注,但是发现使用laravel框架的时候出现了一些小问题,特意实践总结了一些常用情形,希望对大家有所帮助   先理解几个概念: 1>StdClass 对象=&g ...

  4. 从头实现一个koa框架

    koajs是最流行的nodejs后端框架之一,有很多网站都使用koa进行开发,同时社区也涌现出了一大批基于koa封装的企业级框架.然而,在这些亮眼的成绩背后,作为核心引擎的koa代码库本身,却非常的精 ...

  5. springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)

    之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...

  6. .net 表达式返回值和等号赋值的区别

    .net 7.0的新特性中,有一个使用表达式体返回值的操作.请看如下代码: private string _userName=""; public string UserName{ ...

  7. 深入PHP内核之函数和返回值

    1.关于返回值,PHP内核中使用了大量的宏来实现,我们先看一个函数 PHP_FUNCTION  宏的定义(Zend/zend_API.h) #define PHP_FUNCTION ZEND_FUNC ...

  8. 关于ExecuteNonQuery执行的返回值(SQL语句、存储过程)

    因为msdn中说返回受影响的行数: Executes a Transact-SQL statement against the connection and returns the number of ...

  9. 关于ExecuteNonQuery执行存储过程的返回值 、、实例讲解存储过程的返回值与传出参数、、、C#获取存储过程的 Return返回值和Output输出参数值

    关于ExecuteNonQuery执行存储过程的返回值 用到过ExecuteNonQuery()函数的朋友们在开发的时候肯定这么用过. if(cmd.ExecuteNonQuery("xxx ...

随机推荐

  1. javascript回文和类名的检测方法

    回文**           //"123"变为"123321" //"abc321"变为"abcd321123cba" ...

  2. 微信小程序-设备

    网络状态: wx.getNetworkType(OBJECT) 获取网络类型. OBJECT参数说明: wx.getNetworkType({ success: function(res) { var ...

  3. CSS与JavaScript的一些问题汇总

    通过最近的学习,总结了一些问题,可能总结得不够完善,但是好记性不如烂笔头,先记在这儿,后面看到更完整的回答,再进行修改. 1.事件流,如何阻止冒泡事件流:在点击一个按钮时,实则,按的父容器与按钮的父容 ...

  4. MVC架构 使用FastReport

    1.Web.config文件 添加配置 <httpHandlers> <add path="FastReport.Export.axd" verb="* ...

  5. SQL Server Reporting Service(SSRS) 第三篇 SSRS Matrix用法

    以前不是太清楚SSRS的功能,自从最近有了了解之后,发现它的功能的确很强大.对于Matrix,刚开始我竟不知道它到底有什么用,现将通过一个例子中去理解Matrix,以及和分组Group结合使用的便利性 ...

  6. ATC空管系统的实时控制软件系统分析

    什么是ATC空管系统? 空中交通管制的目的是对航空器的空中活动进行有效的管理,维护空中交通秩序,保障空中交通畅通,保证飞行安全和提高飞行效率,防止航空器相撞,防止机场及其附近空域的航空器同障碍物相撞. ...

  7. openssl lhash 数据结构哈希表

    哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...

  8. C#中对IDisposable接口的理解

    http://blog.sina.com.cn/s/blog_8abeac5b01019u19.html C#中对IDisposable接口的理解 本人最近接触一个项目,在这个项目里面看到很多类实现了 ...

  9. shell下>和>>的区别

    >  :如果文件不存在,同上,如果文件存在,先将文件清空,然后将数据填入此文件 >> :如果文件不存在,将创建新的文件,并将数据送至此文件:如果文件存在,则将数据添加在文件后面

  10. spark 安装

    Spark 集群安装 1.上传tar包,解压tar包 tar -zxvf spark-2.0.0-bin-hadoop2.6.tgz 2.修改配置文件(这是yarn模式) 进入 spark/conf ...