Abstract

本系列是关于Koa框架的文章,目前关注版本是Koa v1。主要分为以下几个方面:

  1. Koa源码分析(一) -- generator
  2. Koa源码分析(二) -- co的实现
  3. Koa源码分析(三) -- middleware机制的实现

Koa概括

Koa是基于generator与co之上的新一代中间件框架,它的优势主要集中在以下几个方面

  1. 中间件机制
  2. 封装了request/response, context对象
  3. 使用yield,方便异步编程进行流程控制
  4. 在忽略同步或者异步的情况下,使用try catch可以获取程序运行中的异常(错误处理是服务端程序的核心)

示例代码

var Koa = require('koa');
var app = new Koa();
//添加中间件1
app.use(function *(next){
var start = new Date;
console.log("start=======1111");
yield next;
console.log("end =======1111");
var ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
});
//添加中间件2
app.use(function *(){
console.log("start=======2222");
this.body = 'Hello World';
console.log("end =======2222");
}); app.listen(3000);
/*
start=======1111
start=======2222
end =======2222
end =======1111
GET / - 10
start=======1111
start=======2222
end =======2222
end =======1111
GET /favicon.ico - 5
*/

从上述代码中,我们添加了两个middleware,其中第一个middleware中有一个输入参数next,并通过yield进行调用。通过分析输出的log信息,不难发现,先运行middelware1中的yield之前的代码,然后进入到middleware2中运行,待middleware2运行结束后又回到middleware1中,并运行yield之后的代码。

由于app.use输入的是generator函数,如果熟悉generator函数的同学,或许会说,这是将middleware2作为middleware1中的next参数,依次调用多个generator函数。对,没错,实际运行就是这样的,但是koa框架是如何组织代码实现这样方面的调用,将地狱式调用的异步编程编程这样清晰的结构?请看下文的源码分析

源码分析

Application初始化

function Application() {
if (!(this instanceof Application)) return new Application;
this.env = process.env.NODE_ENV || 'development';
this.subdomainOffset = 2;
// 用于存放中间件,即generator对象
this.middleware = [];
this.proxy = false;
// 获得封装的上下文对象
this.context = Object.create(context);
// 获取封装的请求对象
this.request = Object.create(request);
// 获取封装的响应对象
this.response = Object.create(response);
}

启动服务

listen() {
debug('listen');
// 调用node原生中的创建服务
// 其中callback()是服务创建的核心,具体见下面分析
const server = http.createServer(this.callback());
// 开启服务的监听
return server.listen.apply(server, arguments);
}

添加中间件

app.use = function(fn){
if (!this.experimental) {
// es7 async functions are not allowed,
// so we have to make sure that `fn` is a generator function
assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function');
}
debug('use %s', fn._name || fn.name || '-');
// 将输入的fn依次push到middleware数组中
this.middleware.push(fn);
// 返回this,以便链式调用
return this;
};

node native 创建服务

app.callback = function(){
if (this.experimental) {
console.error('Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed.')
}
// 将中间件按照加入的顺序,实现yield的链式调用,即组织异步调用结构,详细见下面的compose
// co.wrap方法将generator函数转化为Promise
var fn = this.experimental ? compose_es7(this.middleware) : co.wrap(compose(this.middleware));
var self = this; if (!this.listeners('error').length) this.on('error', this.onerror); // 返回node native的请求处理函数
return function handleRequest(req, res){
res.statusCode = 404;
var ctx = self.createContext(req, res);
onFinished(res, ctx.onerror);
fn.call(ctx).then(function handleResponse() {
respond.call(ctx);
}).catch(ctx.onerror);
}
};

中间件异步构建

// 返回一个启动函数
function compose(middleware){
return function *(next){
if (!next) next = noop();
var i = middleware.length;
// 对中间件队列从后遍历,逐个获取对应的generator对象
while (i--) {
// 将后面的generator对象传递给前面中间件的generatorFunction
next = middleware[i].call(this, next);
}
// 返回一个yield,next指向第一个中间件的generator
return yield *next;
}
}
function *noop(){}

这样,我们就从返回的启动函数(generator函数)的yield处指向第一个中间件,然后从之前while循环构成的从前往后的调用链,依次调用下一个中间件,直至最后一个中间件然后再返回。

这边我们再次回到callback()这个启动函数处,调用co.wrap()实现对generator函数的逐步调用。

Koa源码分析(三) -- middleware机制的实现的更多相关文章

  1. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  2. Koa源码分析(一) -- generator

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: 1. Koa源码分析(一) -- generator 2. Koa源码分析(二) -- co的实现 ...

  3. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  4. kernel 3.10内核源码分析--hung task机制

    kernel 3.10内核源码分析--hung task机制 一.相关知识: 长期以来,处于D状态(TASK_UNINTERRUPTIBLE状态)的进程 都是让人比较烦恼的问题,处于D状态的进程不能接 ...

  5. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  6. 【Zookeeper】源码分析之Watcher机制(三)之Zookeeper

    一.前言 前面已经分析了Watcher机制中的大多数类,本篇对于ZKWatchManager的外部类Zookeeper进行分析. 二.Zookeeper源码分析 2.1 类的内部类 Zookeeper ...

  7. Node.js躬行记(19)——KOA源码分析(上)

    本次分析的KOA版本是2.13.1,它非常轻量,诸如路由.模板等功能默认都不提供,需要自己引入相关的中间件. 源码的目录结构比较简单,主要分为3部分,__tests__,lib和docs,从名称中就可 ...

  8. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  9. 【Zookeeper】源码分析之Watcher机制(一)

    一.前言 前面已经分析了Zookeeper持久话相关的类,下面接着分析Zookeeper中的Watcher机制所涉及到的类. 二.总体框图 对于Watcher机制而言,主要涉及的类主要如下. 说明: ...

随机推荐

  1. 很不幸,装win10和Ubuntu双系统还是入坑了

    具体问题描述:在安装Ubuntu的时候,没有发现有与windows共存的提示,直接说本计算机没有安装系统.在安装完成后,发现通过BIOS只有进入Ubuntu的启动项,没有进入Windows的启动项. ...

  2. python_11 装饰器,闭包

    装饰器:本质就是函数,功能是为其他函数添加附加功能 原则: 1.不修改被修饰函数的源代码 2.不修改被修饰函数的调用方式 装饰器的知识储备 装饰器=高阶函数+函数嵌套+闭包 #装饰器: def tim ...

  3. yml使用

    yml: value: 可以用单引号或者双引号引起来,这样就不会出现内容中特殊字符含义. yml中   key:value ,如果value是数字需要和前面的冒号隔一个空格,否则获取不到value

  4. 发布python包

    写python程序时常常要导入各种包,使用其中的模块或功能.我们如果有可以复用的功能或模块也可以发布成包,并安装在自己或他人电脑上,以供自己或别人使用.python发布包的步骤如下: 一.首先为模块创 ...

  5. 实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程

    该例程所用的硬件设备: 直流电机驱动模块YYH-LWZ: H桥 大功率 正反转 刹车 PWM 调速 5/12/24V 12V直流减速电机JGB37-520B:ASLONG JGB37-520B编码器减 ...

  6. PYTHON基础-入门

    变量名可以是数字,字母,下划线,字母不能开头.

  7. mui-H5获取当前手机通讯录

    mui.plusReady(function() { // 扩展API加载完毕,现在可以正常调用扩展API plus.contacts.getAddressBook(plus.contacts.ADD ...

  8. angular.min.js:118 Error: [ng:areq] http://errors.angularjs.org/1.5.8/ng/areq?

    1,错误如图所示 简单说下错误原因是:没有js没有注册进去. 解决方法: 1.看下index.html有没有引入你的js文件. 2.看下app.js有没有注册js,比如我这次就是这步没做好,合并代码时 ...

  9. unique_ptr

    [unique_ptr] unique_ptr 不共享它的指针.它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法.只能移动unique ...

  10. JS-jquery对象和dom对象的属性操作区别

    <label class="">时间1</label> <label class="">时间2</label> ...