用过Koa的,肯定对 Middleware(中间件) 有所了解,那我们就用中间件从现象出发,理解洋葱模型

先自定义两个中间件:

logTime:打印时间戳

module.exports=function() {

returnasyncfunction(ctx, next) {

console.log("next前,打印时间戳:", newDate().getTime())

awaitnext()

console.log("next后,打印时间戳:", newDate().getTime())

}

}

logUrl:打印路由

module.exports=function() {

returnasyncfunction(ctx, next) {

console.log("next前,打印url:", ctx.url)

awaitnext()

console.log("next后,打印url:", ctx.url)

}

}

在 index.js 中use:

constKoa=require('koa')

constapp=newKoa()

constlogTime=require('./middleware/logTime')

constlogUrl=require('./middleware/logUrl')

// logTime

app.use(logTime())

// logUrl

app.use(logUrl())

// response

app.use(asyncctx=> {

ctx.body='Hello World'

})

app.listen(3000)

现在启动服务,然后我们随便访问一个路由,比如 /test,会发现这样一个现象:

两个现象:

  • 中间件的执行了两次

  • 执行顺序奇怪,以next函数为分界点:先use的中间件,next前的逻辑先执行,但next后的逻辑反而后执行

第一反应肯定就是:Koa为什么要这么设计?

正常不应该是中间件按顺序从开始到结束执行吗?

确实,如果说使用中间件的场景不存在前后依赖的情况,从头到尾按顺序链式调用完全没问题。但如果存在依赖的情况呢?如果只链式执行一次,怎么能保证前面的中间件能使用之后的中间件所添加的东西呢?

比如上面两个例子,我在 logUrl 的中间件里,对url进行了处理,加上了一个时间戳,然后我想在 logTime 的中间件里拿到这个时间戳并打印

如果只链式执行一次的话,显然无法实现

然后就是顺序的问题,为什么以next为分界线,先use的中间件 next 之后的逻辑反而后执行呢?

还是上面的例子,如果我们在logUrl 中间件里对url加上去的时间戳,是从数据库里取出来的,logTime 中间件肯定得等 logUrl 跑完了才能拿到对应时间戳。所以如果 logTime 之中的next完成的话,肯定是logUrl这个中间件已经跑完了

因此,上述现象1、2的原因就很清晰了

这样我们可以就确定中间件的执行流必定是如下的一个流程:

  • 外层中间件进行前期处理(next 前的逻辑)

  • 调用next,将控制流交给下个中间件,并await其完成,直到后面没有中间件或者没有next函数执行为止

  • 完成后一层层回溯执行各个中间件的后期处理(next 后的逻辑)

这就是洋葱模型

再放出那两张很经典的图:

Koa中,外层中间件称为上游,内层中间件称为下游,现在我们再回头看官网的描述:

先用我们自己的方式理解之后,官方描述就不会太晦涩了

知道了原因,我们肯定得手动来实现一下洋葱模型,加深理解

实现之前肯定得先看一波源码:

koa

koa-compose

来,一步步分析一波:

首先,先看看middleware在源码里是什么数据类型:

然后按流程看,肯定先进app的listen函数:

创建服务的时候,传入了callback函数的返回值,去看看callback函数:

重点就是这里了,我们上面的分析说明想要实现洋葱模型,下面两点缺一不可:

  • 要把上下文ctx对象和下一个中间件next传给当前的中间件

  • 必须要等待下一个中间件执行完,再执行当前中间件的后续逻辑

而这就是compose函数所做的事情,来自于 koa-compose,这里先暂时不贴源码,有一说一很绕,强行看有点难受

所以,我们可以先按自己的思路来试试:

应该不需要解释吧,这样肯定会报错:

因为执行mw2的时候(也就是mw1里的next),并没有把ctx 和 mw3传给它

那么问题来了:我们怎么才能在调用mw1的next时,把ctx 和 mw2给这个next呢?

那我们肯定就需要对middleware数组里的每个元素重新包装一下了,用什么包装呢?

看个例子:

bind会将当时的参数保留下来,这正是我们所需要的,因此,加上一点小小的改动:

这个时候我们再跑一下代码:

这不就实现了吗?刚刚我留了一个坑就是没放 koa-compose 的源码,下面是源码:

红框的部分就是核心代码,大家可以自己看看,如果感觉很绕,可以对比我上面的例子先理解的,贴一下我简化版的代码:

constmiddleware= []

letmw1=asyncfunction (ctx, next) {

console.log("next前,第一个中间件")

awaitnext()

console.log("next后,第一个中间件")

}

letmw2=asyncfunction (ctx, next) {

console.log("next前,第二个中间件")

awaitnext()

console.log("next后,第二个中间件")

}

letmw3=asyncfunction (ctx, next) {

console.log("第三个中间件,没有next了")

}

functionuse(mw) {

middleware.push(mw)

}

use(mw1)

use(mw2)

use(mw3)

letfn=function (ctx) {

returndispatch(0)

functiondispatch(i) {

letcurrentMW=middleware[i]

if(!currentMW) {

return

}

returncurrentMW(ctx, dispatch.bind(null, i+1))

}

}

fn()

OK啦,这样就大工告成了

总结一下吧:一开始通过现象和场景反推,明白了什么时洋葱模型,以及为什么Koa需要使用洋葱模型,最后就是利用源码,简化实现了一下洋葱模型

说说你对koa中洋葱模型的理解?相关推荐

  1. Koa的洋葱模型到底是什么?

    什么是Koa? Koa是一个精简的node框架,被认为是第二代Node框架,其最大的特点就是独特的中间件流程控制,是一个典型的洋葱模型,它的核心工作包括下面两个方面: 将node原生的req和res封 ...

  2. KOA --- 2. 洋葱模型

    一.什么是koa的洋葱模型 特点: 独特的中间件流程控制,就是典型的洋葱模型 二.解析洋葱模型 在两个中间件函数,用 next 隔开 对应的代码Demo const Koa = require('ko ...

  3. 洋葱模型php,理解Koa洋葱模型

    中间件特性 | | | middleware 1 | | | | +-----------------------------------------------------------+ | | | ...

  4. koa 中间件洋葱模型源码分析

    中间件基本使用 app.use(async(ctx,next)=>{ctx.state={username:'jeff'};await next();... })app.use(async(ct ...

  5. 聊一聊KOA的洋葱模型

    Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小.更富有表现力.更健壮的基石. 通过利用 async 函数,Koa ...

  6. Koa核心—洋葱模型

    洋葱模型 koa框架的业务流程是一个完全的异步编程模型,通过ctx上下文对象来贯穿http的上下游.那么我们最重要的就是理解洋葱模型. const koa = require("Koa&qu ...

  7. 洋葱模型php,聊一聊KOA的洋葱模型

    Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小.更富有表现力.更健壮的基石. 通过利用 async 函数,Koa ...

  8. KOA 初探 洋葱模型

    KOA 官网链接 当然,如果你有express的基础就最好了,毕竟是原班人马开发 常用的指令 npm install koa npm install nodemon touch app.js node ...

  9. CSS中盒模型的理解

    1.基本的盒模型知识 CSS css盒子模型 又称框模型 (Box Model) ,包含了元素内容(content).内边距(padding).边框(border).外边距(margin)几个要素.如 ...

最新文章

  1. 【BZOJ-3712】Fiolki LCA + 倍增 (idea题)
  2. 姚班天才漆子超、IOI金牌得主陈启峰等联手进军AR领域:全员顶尖科技公司技术背景,打造“5G+AI+AR”先锋...
  3. Python网络编程:IO多路复用
  4. 英语关于计算机的作文600字,有关电脑的作文600字
  5. win7 IIS7.5 HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态...
  6. Python CSV 中查找指定字符串
  7. 一个 Task 不够,又来一个 ValueTask ,真的学懵了!
  8. 深度学习去燥学习编码_我们问了15,000个人,他们是谁,以及他们如何学习编码
  9. redis学习笔记-安装与入门
  10. IE浏览器开发人员工具怎么使用
  11. 转:libev和libevent的设计差异
  12. Hibernate中常见问题 No row with the given identifier exists问题
  13. 有linux服务器用来干什么用的,linux集群能干什么
  14. 1012 数字分类 (20 分)—PAT (Basic Level) Practice (中文)
  15. nagios监控php使用情况,给nagios增加监控当前php进程数的插件,并用pnp出图
  16. Windows,bat批量ping脚本
  17. hdu 1427 24点暴力dfs
  18. 能上QQ但是打不开网页
  19. 如何提高英文的科研写作能力——施一公
  20. python爬虫|post的响应,利用python实现有道翻译在线翻译

热门文章

  1. 【C++错误】VS调试出现0xC00000FD:Stack overflow溢出
  2. java ueditor 图片上传加水印_百度ueditor上传图片加水印的例子
  3. 程序员CMD命令大全
  4. 达人深度评测vivos7e和OPPOK7x参数对比 vivos7e和OPPOK7x哪个好
  5. 33岁的外行妈妈,转行金融业可行吗?(此贴仅限于个人感悟)
  6. Java实现台阶问题
  7. 折叠屏已经不新鲜了,明天你可能会用上“伸缩屏幕”!
  8. 与一汽密谈 南汽罗孚点将起航
  9. 离线安装clamav
  10. CentOs解决下载速度慢 更换下载源