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前的代码,并返回一个对象,然后调用next方法,并将ret对象传入。如果第一个generator迭代完成,再调用返回的第一个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, 1)

  // we wrap everything in a promise to avoid promise chaining,  // which leads to memory leak errors.  // see https://github.com/tj/co/issues/180return 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框架继续执行剩余代码。

第一次写这么长的,感觉有点渣...
时间: 2024-10-18 03:14:34

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

委托异步返回值

1:如何新起线程 新起一个线程的方法,可以使用Thread,BackgroundWorker ,ThreadPool,控件.BeginInvoke,委托.BeginInvoke,Timer. 2:异步调用返回值 上码: using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.

支付宝异步返回无操作问题

这两天做支付时遇到订单支付完成后订单状态无修改的情况, 各种奋斗解决终于搞定,现在拿出来分享下希望遇到同样问题的可以快速解决 支付成功后随手关闭支付成功页面,这样需要调用异步页面来修改订单状态,但是页面不接受xml post 直接用网址打开异步页面会返回错误页面.“从客户端****中检测到有潜在危险的 Request.Form 值” 这种情况需要您在web.config 中添加<httpRuntime requestValidationMode="2.0" /> 和<

ajax同步异步设置导致的问题及return返回值(返回ajax请求的数据)

一.ajax同步异步设置导致父子页面数据更新不一致问题. 1.在修复入宿退宿功能时,发现点击退宿.入宿按钮时.父页面数据更新了但是子页面数据要重新打开才能更新.如下图: 因为数据刷新函数如下:父页面刷新函数findRoomPeople()采用的是异步,所以,操作的数据还没更新,子页面刷新函数loadRoomInfo()就已经执行,所以获取到的数据还是之前的数据. <script> roomHandle: function () { /*刷新数据变化*/ parent.cootoo_room.f

特别要检查Java文件操作相关方法的返回值

今天遇到一个很狗血的问题,一个功能在开发环境没有问题,但在生产环境出错了. 代码如下:                 ...                 File tmpFile = new File(fileTmpPath); File newFileTarget = new File(filePath); tmpFile.renameTo(newFileTarget); // 修改新文件的权限 FileManageHelper.chmod(newFileTarget); .... 最后

asp.net后台操作javascript:confirm返回值

在asp.net中使用confirm可以分为两种: 1.没有使用ajax,confirm会引起也面刷新 2.使用了ajax,不会刷新 A.没有使用ajax,可以用StringBuilder来完成. (一)asp.net用StringBuilder控制后台操作javascript:confirm返回值,此方法比较烦琐1.后台启动事件 StringBuilder sb = new StringBuilder();        sb.Append("<script language='java

程序的转化 &amp; 明确的初始化操作 &amp; 参数的初始化 &amp; 返回值的初始化

一丶程序的转化考察以下代码: 1 X Foo() 2 { 3 X xx; 4 //... 5 return xx; 6 } 看到这个, 你可能会有两个想法:1. 每次 Foo() 被调用, 就会传回 xx 的值.2. 如果 class X 定义了一个 copy constructor, 那么当 Foo() 被调用时, 保证该 copy constructor 也会被调用. 这两个假设的真实性实际都要以 class X 如何定义而定, 在一个高品质的 C++ 编译器中, 以上两个假设都是错误的.

php 判断table 是否存在 根据返回值继续下一步的操作

根据sql命令创建数据库或者数据表时候,判断库或者表是否存在比较重要. //要创建的表是否已经存在 function isHaveTable( $dbName,$tableN, $con)  //数据库名, 表名, 连接参数 { //数据库存在的情况下 才开始判断表是否存在 $db_selected = mysql_select_db($dbName, $con); if (!$db_selected) { die ("Can\'t use $dbname : " . mysql_er

异步上传文件并获得返回值(完全跨域)

http://blog.csdn.net/lrz1011/article/details/7913992 异步上传文件并获得返回值(完全跨域)AJAX可以进行数据的异步请求,但对于文件和跨域问题却束手无策. Jsonp可以进行跨域数据的异步请求,但同样不能使用于文件. <form>表单可以进行跨域数据和文件的上传,但却会使页面跳转. 那么如何同时实现“异步”+“跨域”+“文件”+“返回值”这几个特性呢?方法如下: 原理: 将<form>表单通过一个iframe来submit,也就是

C# 调用存储过程操作 OUTPUT参数和Return返回值

本文转载:http://www.cnblogs.com/libingql/archive/2010/05/02/1726104.html 存储过程是存放在数据库服务器上的预先编译好的sql语句.使用存储过程,可以直接在数据库中存储并运行功能强大的任务.存储过程在第一应用程序执行时进行语法检查和编译,编译好的版本保存在高速缓存中.在执行重复任务时,存储过程可以提高性能和一致性.由于存储过程可以将一系列对数据库的操作放在数据库服务器上执行,因而可以降低Web服务器的负载,提高整个系统的性能. 1.创