8000 koa源码分析(五) - koa 2.0 · Issue #10 · brunoyang/blog · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
koa源码分析(五) - koa 2.0 #10
Open
@brunoyang

Description

@brunoyang

co的README里,tj写道co是async/await的一块垫脚石,没想到好时代这么快就来了,我们已经可以借助babel写出形似同步的异步代码了(需开启experimental模式)。而借着这股春风,koa2.0也来了。

我们还是先来看一下什么是async/await函数吧。

async函数就是generator + promise,只不过是将yield换成了await,但是比yield好理解,并且不用next来显式地执行下一步。async 可以声明一个异步函数,此函数需要返回一个 Promise 对象。await 可以等待一个 Promise 对象 resolve,并拿到结果。我们来看一个例子(引用自阮一峰老师的ECMAScript 6 入门)。

const readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) reject(error);
      resolve(data);
    });
  });
};

const asyncReadFile = async function (){
  const f1 = await readFile('a.txt');
  const f2 = await readFile('b.txt');
  console.log(f1.toString());
  console.log(f2.toString());
};

可以看到,async和promise是紧密结合在一起的,await的结果就是被修饰函数的resolve值,那似乎没有方法接收reject呀,所以,最好是将await包裹在try/catch中,用来接收reject。

const asyncReadFile = async function (){
  try{
    const f1 = await readFile('a.txt');
    const f2 = await readFile('b.txt');
    console.log(f1.toString());
    console.log(f2.toString());
  } catch(e) {
    console.log('failed!');
  } 
};

有了async,能让我们更好地书写异步代码,就是这么的方便。

说了这么多async,我们再来看koa。koa2.0相对于1.0,转变体现在将var换成了let及const,使用箭头函数简化书写,不再支持generator,以及对于中间件的处理上。

中间件有一个很形象的比喻,就像一个洋葱,一个请求从最外面那一层进入洋葱,一路上进过一层层的中间件,到达洋葱心之后,请求完成了任务,又派了响应出去,带上需要返回的数据,一层层地返回最外面。而在进入出来的过程中,中间件会对请求和响应做『手脚』,比如对请求检验cookie,对响应加etag。那中间件是怎么知道来的到底是请求还是响应呢?其实中间件不需要知道,这是因为请求肯定早于响应,所以koa的中间件的做法就是将函数的上半部分用来处理请求,下半部分用来处理响应(这里只是打比方)。而区分『上半部分』和『下半部分』的分界线,在1.0是yield next(),在2.0中,就是await next()或是return next().then()。

说完原理,再来看koa-compose,最关键的只有一步

return Promise.resolve(fn(context, function next() {
  return dispatch(i + 1)
}))

Promise.resolve的作用是将一个普通的对象转换成promise对象,防止上一步放回一个普通函数,造成中间件链路的断裂。

为了更好地理解如何使用中间件,我们来写一个访客记录的中间件。

首先分析一下需求,我们认为请求中带有visited字段cookie的请求是来自曾经访问过本网站的游客,就在数据库中增加一次访问量,并将该条请求打个标记便于后续操作。若没有visitedcookie,就在响应增加visitedcookie。

module.exports = async function (ctx, next) {
  ctx.countNum = await count();
  const visited = ctx.cookies.get('visited') ? true : false;

  await next();

  if (!visited) {
    setCookie(ctx, 'visited');
  }
}

function count() {
  const Count = new mongoose.model('count');

  Count.add()
  .then(function() {
    return Count.findCountNum();
  })
  .then(function(num) {
    return Promise.reslove(num);
  });
} 

function setCookie(ctx, name) {
  ctx.cookies.set(name, 'what ever');
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0