中间件特性

| |

| middleware 1 |

| |

| +-----------------------------------------------------------+ |

| | | |

| | middleware 2 | |

| | | |

| | +---------------------------------+ | |

| | | | | |

| action | action | middleware 3 | action | action |

| 001 | 002 | | 005 | 006 |

| | | action action | | |

| | | 003 004 | | |

| | | | | |

+---------------------------------------------------------------------------------------------------->

| | | | | |

| | | | | |

| | +---------------------------------+ | |

| +-----------------------------------------------------------+ |

+----------------------------------------------------------------------------------+

先写一段贯穿全文的koa的代码

const Koa = require('koa');

let app = new Koa();

const middleware1 = async (ctx, next) => {

console.log(1);

await next();

console.log(6);

}

const middleware2 = async (ctx, next) => {

console.log(2);

await next();

console.log(5);

}

const middleware3 = async (ctx, next) => {

console.log(3);

await next();

console.log(4);

}

app.use(middleware1);

app.use(middleware2);

app.use(middleware3);

app.use(async(ctx, next) => {

ctx.body = 'hello world'

})

app.listen(3001)

// 输出1,2,3,4,5,6

await next()使每个middleware分成,前置操作,等待其他中间件操作可以观察到中间件的特性有:

上下文ctx

await next()控制前后置操作

后置操作类似于数据解构-栈,先进后出

promise 的模拟实现

Promise.resolve(middleware1(context, async() => {

return Promise.resolve(middleware2(context, async() => {

return Promise.resolve(middleware3(context, async() => {

return Promise.resolve();

}));

}));

}))

.then(() => {

console.log('end');

});

从这段模拟代码我们可以知道next()返回的是promise,需要使用await去等待promise的resolve值。promise的嵌套就像是洋葱模型的形状就是一层包裹着一层,直到await到最里面一层的promise的resolve值返回。

思考:

如果next()不加await执行顺序是什么呢?

在这个例子里面如果只是next()执行顺序跟await next()是一样的,因为next的前置操作是同步的

如果前置操作是异步的操作呢?

const p = function(args) {

return new Promise(resolve => {

setTimeout(() => {

console.log(args);

resolve();

}, 100);

});

};

const middleware1 = async (ctx, next) => {

await p(1);

// await next();

next();

console.log(6);

};

const middleware2 = async (ctx, next) => {

await p(2);

// await next();

next();

console.log(5);

};

const middleware3 = async (ctx, next) => {

await p(3);

// await next();

next();

console.log(4);

};

// 输出结果:1,6,2,5,3,4

当程序执行到middleware1,执行到await p(1)等待promise值返回跳出然后到下一个事件循环时,执行next()也就是执行到middleware2,再执行到await p(2)等待promise值返回跳出middleware2,回到middleware1继续执行console.log(6),以此类推输出顺序为1.6.2.5.3.4

Promise的嵌套虽然可以实现中间件流程,但是嵌套的代码会产生可维护性和可读性的问题,也带来中间件扩展的问题。

Koa.js中间件引擎是有koa-compose模块来实现的,也就是Koa.js实现洋葱模型的核心引擎。

koa-compose 实现

this.middleware = [];

use(fn) {

this.middleware.push(fn);

……

}

callback() {

const fn = compose(this.middleware);

……

}

function compose (middleware) {

return function (context, next) {

let index = -1

return dispatch(0)

function dispatch (i) {

if (i <= index) return Promise.reject(new Error('next() called multiple times'))

index = i

let fn = middleware[i]

if (i === middleware.length) fn = next

if (!fn) return Promise.resolve()

try {

return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));

} catch (err) {

return Promise.reject(err)

}

}

}

}

Koa实现的代码非常简洁,我们在使用use的时候将middleware存在一个数组里面,当拦截到请求时执行callback方法,callback中调用了compose,compose方法使用递归执行中间件,遍历完成返回promise.resolve(),实际最后执行的代码也是上面所讲的promise嵌套的形式。

扩展:Await与Generator

通常我们的都会说await阻塞后面的操作等待promise的resolve返回值或者其他值,如果没有await这个语法糖,要怎么去实现呢?这个等待的过程是怎么控制的呢?

Generator

Generator实际上是一个特殊的迭代器

let gen = null;

function* genDemo(){

console.log(1)

yield setTimeout(()=>{

console.log(3);

gen.next();// c

},100)

console.log(4)

}

gen = genDemo();// a

gen.next(); // b

a. 调用generator,该函数不执行,也就是还没有输出1,返回的是指向内部状态的遍历对象。

b. generator函数开始执行,输出1,遇到第一个yeild表达式停下来,调用gen.next()返回一个对象{value: 10, done:false},这里的value表示setTimeout的一个标识值,也就是调用clearTimeout的参数,是一个数字。done表示遍历还没有结束。100毫秒后输出3;

c. Generator函数从上次在yeild停止的地方一直执行到函数结束(没有其他的yeild),输出4,返回{value: undefined,done:true},表示遍历结束。

可以看到yeild有控制代码进度的作用,是不是跟await有异曲同工之妙

来看下await编译成generator形式的代码,虽然多了一些代码,但是我们可以把_asyncToGenerator(function*() {……}调用generator,把asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);看成是gen.next();就很容易理解了。

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {

try {

var info = gen[key](arg);

var value = info.value;

} catch (error) {

reject(error);

return;

}

if (info.done) {

resolve(value);

} else {

Promise.resolve(value).then(_next, _throw);

}

}

function _asyncToGenerator(fn) {

return function() {

var self = this,

args = arguments;

return new Promise(function(resolve, reject) {

var gen = fn.apply(self, args);

function _next(value) {

asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);

}

function _throw(err) {

asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);

}

_next(undefined);

});

};

}

const middleware1 =

/*#__PURE__*/

(function() {

var _ref = _asyncToGenerator(function*(ctx, next) {

console.log(1);

yield next();

console.log(6);

});

return function middleware1(_x, _x2) {

return _ref.apply(this, arguments);

};

})();

const middleware2 =

/*#__PURE__*/

(function() {

var _ref2 = _asyncToGenerator(function*(ctx, next) {

console.log(2);

yield next();

console.log(5);

});

return function middleware2(_x3, _x4) {

return _ref2.apply(this, arguments);

};

})();

const middleware3 =

/*#__PURE__*/

(function() {

var _ref3 = _asyncToGenerator(function*(ctx, next) {

console.log(3);

yield next();

console.log(4);

});

return function middleware3(_x5, _x6) {

return _ref3.apply(this, arguments);

};

})();

Promise.resolve(

middleware1(

context,

/*#__PURE__*/

_asyncToGenerator(function*() {

return Promise.resolve(

middleware2(

context,

/*#__PURE__*/

_asyncToGenerator(function*() {

return Promise.resolve(

middleware3(

context,

/*#__PURE__*/

_asyncToGenerator(function*() {

return Promise.resolve();

})

)

);

})

)

);

})

)

).then(() => {

console.log("end");

});

洋葱模型php,理解Koa洋葱模型相关推荐

  1. 随机森林算法参数解释及调优 转胡卫雄 RF模型可以理解成决策树模型嵌入到bagging框架,因此,我们首先对外层的bagging框架进行参数择优,然后再对内层的决策树模型进行参数择优

    RF参数择优思想:RF模型可以理解成决策树模型嵌入到bagging框架,因此,我们首先对外层的bagging框架进行参数择优,然后再对内层的决策树模型进行参数择优.在优化某一参数时,需要把其他参数设置 ...

  2. 简述对css盒子模型的理解_css 盒子模型理解

    盒子模型是html+css中最核心的基础知识,理解了这个重要的概念才能更好的排版,进行页面布局.下面是自己积累和总结的关于css盒子模型的知识^_^,希望对初学者有用. 一.css盒子模型概念 CSS ...

  3. 简述对css盒子模型的理解_CSS盒子模型的理解

    一 css盒模型由两个盒子组成,外在的控制是否换行的盒子,以及内在的控制元素内容的盒子. 比如:display: inline-block, 则它的外在的盒子就是inline也就是不占据一行,而blo ...

  4. 通俗理解LDA主题模型

    0 前言 印象中,最开始听说"LDA"这个名词,是缘于rickjin在2013年3月写的一个LDA科普系列,叫LDA数学八卦,我当时一直想看来着,记得还打印过一次,但不知是因为这篇 ...

  5. [css] 说说你对css盒子模型的理解

    [css] 说说你对css盒子模型的理解 css盒模型由两个盒子组成,外在的控制是否换行的盒子,以及内在的控制元素内容的盒子.比如:display: inline-block, 则它的外在的盒子就是i ...

  6. LDA通俗理解LDA主题模型

    转自:http://blog.csdn.net/v_july_v/article/details/41209515#t13 通俗理解LDA主题模型 0 前言 印象中,最开始听说"LDA&qu ...

  7. 通俗理解LDA主题模型(转载自 v_JULY_v 大佬)

    通俗理解LDA主题模型 原文:https://blog.csdn.net/v_july_v/article/details/41209515 0 前言 印象中,最开始听说"LDA" ...

  8. 理解 LDA 主题模型

    前言 gamma函数 0 整体把握LDA 1 gamma函数 beta分布 1 beta分布 2 Beta-Binomial 共轭 3 共轭先验分布 4 从beta分布推广到Dirichlet 分布 ...

  9. 通俗理解LDA主题模型(转)

    from :http://blog.csdn.net/v_july_v/article/details/41209515 也推荐:LDA漫步指南 通俗理解LDA主题模型 0 前言 印象中,最开始听说& ...

最新文章

  1. “真”5G标准出炉!不止是速度,这些难以置信的改变将彻底颠覆你的生活
  2. IMAX融资5000万美元,三年内要打造25个VR项目
  3. AdaBoostClassifier实战
  4. 你现在的CSS水平处于什么等级?
  5. hexo的yilia主题鼠标点击出现小红心
  6. wordpress列表页调用浏览器,wordpress显示文章浏览量!
  7. TypeError: 'NoneType' object is not subscriptable
  8. shell timeout
  9. HTML下的list w3c,W3.CSS Lists (列表)
  10. 鼠标监听地图让我创造出个性的风格(地图开发)
  11. 纷杂的Spring-boot-starter: 3 数据访问与spring-boot-starter-jdbc
  12. SpringBoot+Vue本地实现微信公总号web端的微信登录获取用户信息,前后端分离
  13. 数据湖(十一):Iceberg表数据组织与查询
  14. 计算机加密无法连接打印机,0x00000006无法连接打印机怎么办
  15. 问题:控制台报错style-helper.mjs?d002:125 Uncaught (in promise) TypeError: Cannot read properties
  16. 生活与美食,真的如你想象的那么简单吗
  17. 金龙云介绍——城轨云相关
  18. 嵌入式 linux下proc目录下的文件详解
  19. 社团管理系统(part2)
  20. Linux:命令gedit主要作用是什么?

热门文章

  1. APP长期处于后台手机打开多个APP后进程被杀
  2. python是如何实现进程池和线程池的_进程、线程、线程池和协程如何理解?
  3. java如何转成jar包,修改及反编译可运行Jar包实现过程详解
  4. java中怎么制作单选框_java代码swing编程 制作一个单选按钮的Frame
  5. Activity的task相关
  6. ArcGIS Desktop新建postgresql版sde(10.4.1)的连接
  7. linux下安装配置redis服务
  8. yum安装与源码编译安装实际使用区别
  9. rsync定时任务引起cpu负载高
  10. iOS开发之Objective-C与JavaScript的交互