看完 Koa 源码我把核心思想应用到了公司项目
本文你可以学到
理解 koa2洋葱模型核心源码compose函数实现理解函数式编程衍生范式——面向切面编程 除了看源码,实战把洋葱模型思想应用到 SDK项目中
Koa2 源码实现

我想很多小伙伴应该都知 Koa 有一个洋葱模型的概念,
通过它控制中间件内部内容的执行顺序。先来复习一下中间件结构。
function?logger(ctx,next){}
类似这种的每个函数都是一个中间件。然后这些中间件会被存放到一个 middlewares 中间件数组中。
然后依赖的核心库是 koa-compose,去完成整个 middlewares 数组中函数的执行,他不是普通的遍历依次执行过程,里面有一些特殊实现,重点关注下 源码中 dispatch 函数实现。
源码如下,对核心代码部分进行了注释讲解
//?compose?函数参数是前面提到的?middlewares?中间件数组
function?compose(middleware)?{
??//?参数校验:判断middleware是否为数组
??if?(!Array.isArray(middleware))
????throw?new?TypeError("Middleware?stack?must?be?an?array!");
??//?数组内容校验:中间件数组中每一项必须是一个方法
??for?(const?fn?of?middleware)?{
????if?(typeof?fn?!==?"function")
??????throw?new?TypeError("Middleware?must?be?composed?of?functions!");
??}
??//?返回一个方法,这个方法就是compose的结果
??//?外部可以通过调用这个方法来开中间件数组的遍历
??//?参数形式和普通中间件一样,都是context和next
??return?function?(context,?next)?{
????return?dispatch(0);?//?开始中间件执行,从数组第一个开始
????//?中间件执行函数
????function?dispatch(i)?{
??????let?fn?=?middleware[i];?//?取出需要执行的中间件
??????//?如果i等于数组长度,说明数组已经执行完了
??????if?(i?===?middleware.length)?{
????????fn?=?next;?//?fn等于外部传进来的next,结束执行
??????}
????????????//?如果外部没有传结束执行的next,直接就resolve
??????if?(!fn)?{
????????return?Promise.resolve();
??????}
??????//?执行中间件,注意传给中间件接收的参数应该是context和next
??????//?传给下一个中间件的next是函数,一定注意这里是使用的bind?dispatch.bind(null,?i?+?1)
??????//?所以中间件里面调用?next?的时候其实调用的是dispatch(i?+?1),也就是执行下一个中间件
??????try?{
????????return?Promise.resolve(fn(context,?dispatch.bind(null,?i?+?1)));
??????}?catch?(err)?{
????????return?Promise.reject(err);
??????}
????}
??};
}
对这段代码的实现进行了详细的注释,再次强调一下代码的核心部分
return?Promise.resolve(fn(context,?dispatch.bind(null,i+1)))
fn 执行的第二个参数实际是中间件数组中的函数引用(使用了 bind 函数),在中间件内部调用 next 实际调用的是 dispatch(i+1),也就是下一个中间件。
洋葱模型思想在项目中的应用
需求描述
我们要提供一个 Node.js 的 SDK,在这个 SDK 中我们提供了一系列功能,本文要讲的是其中一个小部分:请求函数中聚合中间件实现,SDK 使用者发起一个请求会调用 SDK 的 request 请求函数,这个函数我们应该怎么封装呢?
SDK.request 调用理论上会执行下面的一系列中间件函数。

如图所示,包括的功能有 危险字符过滤,日志记录,响应处理等等(这里就不一一列举了),它的实现正需要一个洋葱模型的机制,上分支是洋葱进入时前期处理的函数,然后交给并等待其他中间件处理面,下分支是扒开洋葱后期处理的过程,
理论科普:洋葱模型也叫面向切面编程。AOP 为 Aspect Oriented Programming 的缩写,中文意思为:面向切面编程,它是函数式编程的一种衍生范式。面向切面编程的是在现有程序中,加入或减去一些功能(函数中间件)不影响原有的代码功能。比如我们的
request需求中去除log记录中间件。
分析与代码实现
首先我们定义一个存放中间件的 moduleList定义 compose函数执行 dispatch(0)以及在dispatch函数内部调用fn.call(null,context,dispatch.bind(null,i+1))
const?moduleList?=?[
????require('./dangerQuery'),
????require('./log'),
????...//?省略一部分
????require('./resHandler')
];
export?const?compliations?=?(ctx:Context,next:Next)=>{
????const?context?=?{ctx,next};
????cosnt?composeFn?=?compose(moduleList);
????composeFn(context);
}
function?compose?=?(list:Array<Function>)=>{
????if(!Array.isArray(list)){
?????????throw?new?TypeError("ModuleList?must?be?an?array!");
????}
????for(const?fn?of?list){
????????if(typeof?fn?!==?'function'){
??????????????throw?new?TypeError("ModuleList?must?be?composed?of?functions!");
????????}
????}
????return?function(context:{ctx:Context,next:Next}){
????????const?dispatch?=?async?(i:number)=>{
????????????if(list.length>i){
????????????????const?fn?=?list[i];
????????????????await?fn.call(null,context,dispatch.bind(null,i+1))
????????????}
????????}
????????return?dispatch(0)
????}
}
中间件实现很多注意的点,本文只是想把洋葱模型部分思想理解,并应用起来,实际每个中间件内部要支持可插拔机制和开关机制的;并且
moduleList中最后一个中间件函数,实际函数的第二个next参数已经为空了,不要再次执行,如果SDK是基于egg,midway等进行封装的,最后一个中间件内部应使用await context.next()
感悟
好东西就要用起来,除了这部分,自己项目中用到洋葱模型思想的还比较多的,并且开源项目中也很多,比如 Webpack,Redux。可以看看他们的使用有哪些巧妙之处。我们在看源码的过程中。不要为了看源码而看源码,最好看懂后应用起来才会真的掌握 面试过程中如果写到了 koa,洋葱模型肯定是一个必考项,如果能把原理说清楚,并举例将思想用到了自己项目中我觉得也是一个加分项。
参考文章
https://juejin.cn/post/7078905984772489247 https://github.com/koajs/koa
- EOF -
关注「程序员的那些事」加星标,不错过圈内事
点赞和在看就是最大的支持?
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 总书记带领我们“办好自己的事” 7904684
- 2 金正恩单膝跪地吊唁俄驻朝大使 7809341
- 3 再次提醒:中国公民近期避免前往日本 7713122
- 4 近15万亿新增贷款去哪了 7616949
- 5 蜜雪冰城开始卖早餐了 7522901
- 6 四川雅安现不明巨响 应急部门回应 7424014
- 7 网警:男子AI生成车展低俗视频被拘 7332121
- 8 狂甩45次 无汞体温计才降到36℃ 7232132
- 9 “九天”无人机成功首飞 7141100
- 10 立冬以来最大规模雨雪上线 7045842



![Wiz_H张子豪 换换风格,怎么说?[笑哈哈] ](https://imgs.knowsafe.com:8087/img/aideep/2023/6/9/0d250136d4e46c0f9b287652695b99df.jpg?w=250)



程序员的那些事
