koa系列koa洋葱模型和其compose原理
什么是洋葱模型
先来看一个 demo
-
const Koa = require('koa');
-
const app = new Koa();
-
-
// 中间件1
-
app.use((ctx, next) => {
-
console.log("<<<1");
-
next();
-
console.log("1>>>");
-
});
-
-
// 中间件 2
-
app.use((ctx, next) => {
-
console.log("<<<2");
-
next();
-
console.log("2>>>");
-
});
-
-
// 中间件 3
-
app.use((ctx, next) => {
-
console.log("<<<3");
-
next();
-
console.log("3>>>");
-
});
-
app.listen(8000, '0.0.0.0', () => {
-
console.log(`Server is starting`);
-
});
输出的结果是:
-
<<<1
-
<<<2
-
<<<3
-
3>>>
-
2>>>
-
1>>>
在 koa 中,中间件被 next() 方法分成了两部分。next() 方法上面部分会先执行,下面部门会在后续中间件执行全部结束之后再执行。可以通过下图直观看出:
在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能,比如错误处理、Session 处理等等。其处理顺序先是 next() 前请求(Request,从外层到内层)然后执行 next() 函数,最后是 next() 后响应(Response,从内层到外层),也就是说每一个中间件都有两次处理时机。
深入 Koa 洋葱模型
我们以文章开始时候的 demo 来分析一下 koa 内部的实现。
-
-
const Koa = require('koa');
-
const app = new Koa();
-
-
// 中间件1
-
app.use((ctx, next) => {
-
console.log("<<<1");
-
next();
-
console.log("1>>>");
-
});
-
-
// 中间件 2
-
app.use((ctx, next) => {
-
console.log("<<<2");
-
next();
-
console.log("2>>>");
-
});
-
-
// 中间件 3
-
app.use((ctx, next) => {
-
console.log("<<<3");
-
next();
-
console.log("3>>>");
-
});
-
app.listen(8000, '0.0.0.0', () => {
-
console.log(`Server is starting`);
-
});
use 方法
use 方法就是做了一件事,将用户自定义的函数模块收集到 middleware 中间件数组里
-
use(fn) {
-
this.middleware.push(fn);
-
}
listen 方法
执行 app.listen 方法的时候,其实是 Node.js 原生 http 模块 createServer 方法创建了一个服务,其回调为 callback 方法。
-
listen(...args) {
-
//请求进来后,我们在这里处理所有的中间件函数
-
const server = http.createServer(this.callback());
-
server.listen(...args);
-
}
callback 方法
callback方法就会将use方法存储的middleware中间件数组传给compose函数,callback方法方法内部借用了compose函数实现了对所有中间件函数的调用。
一个简易版的callback方法
-
callback() {
-
//向 compose传递中间件函数数组
-
const fnMiddleWare = this.compose(this.middleware);
-
-
//调用compose返回的方法,实现对所有中间件的调用
-
const handleRequest = (req, res) => {
-
fnMiddleWare()
-
.then(() => {
-
console.log("end");
-
res.end("my koa");
-
})
-
.catch((err) => {
-
res.end(err.message);
-
console.log("err", err);
-
});
-
};
-
return handleRequest;
-
}
compose方法
compose方法是洋葱模型的核心,其返回一个函数
-
compose(middleware) {
-
return function () {
-
-
return xxxxx;
-
};
-
}
因此,在callback函数中,我们用变量进行了接收并调用
-
callback() {
-
const fnMiddleWare = this.compose(this.middleware);
-
fnMiddleWare()
-
.....
-
return xxxx;
-
}
compose返回的函数体内,定义了一个dispath方法,我们在调用compose返回的函数时,实际在调用dispath这个方法
-
compose(middleware) {
-
return function () {
-
const dispatch = (index) => {
-
......
-
};
-
return dispatch(0);
-
};
-
}
现在,我们来看看 dispatch的主要逻辑
-
// 异步递归遍历调用中间件处理函数
-
compose(middleware) {
-
return function () {
-
const dispatch = (index) => {
-
if (index >= middleware.length) return Promise.resolve();
-
const fn = middleware[index];
-
return Promise.resolve(
-
fn({}, () => dispatch(index 1)));
-
};
-
return dispatch(0);
-
};
-
}
当我们第一次执行fnMiddleWare()函数时,可以看出,实际执行的是dispatch(0)这个函数。
dispatch( 0 )运行时
-
const dispatch = (index:0) => {
-
if (index:0 >= middleware.length:3) return Promise.resolve();
-
const fn = middleware[index:0];
-
return Promise.resolve(
-
fn({}, () => dispatch(index:0 1)));
-
};
我们逐步分析:
-
//如果index 大于 中件间函数的数量,返回一个成功状态的PROMISE
-
if (index:0 >= middleware.length:3) return Promise.resolve();
const fn = middleware[index:0];
此时,fn为中间件的第一个回到函数
-
(ctx, next) => {
-
console.log("<<<1");
-
next();
-
console.log("1>>>");
-
}
-
return Promise.resolve(
-
fn({}, () => dispatch(index:0 1))
-
);
最后一步,返回了一个Promise,因此,在callback函数内,我们需要使用。.then的方式来接收这个最终返回结果。我们中间件的所有执行逻辑,其实都在Promise.resolve里。
当调用 fn({ }, () => dispatch(index:0 1) ) 时
实际执行了这个匿名函数
-
(ctx, next) => {
-
console.log("<<<1");
-
next();
-
console.log("1>>>");
-
}
ctx此时的值是{ } ,这里的next的值是 () => dispatch(index:0 1) 匿名函数
当这个匿名函数运行时,console.log("<<<1")正常执行,我们的浏览器会打印出
<<<1
当执行到这个next() 时,() => dispatch(index:0 1) 匿名函数执行,触发
dispatch(1)
此时,第一个fn函数终止运行,开始执行 dispatch(1) ,因此console.log(" 1>>> ")暂时不执行
dispatch(1) 运行时
-
const dispatch = (index:1) => {
-
if (index:1 >= middleware.length:3) return Promise.resolve();
-
const fn = middleware[index:1];
-
return Promise.resolve(
-
fn({}, () => dispatch(index:1 1)));
-
};
同理,此时会执行middleware中间函数数组的第二个匿名回调函数
-
(ctx, next) => {
-
console.log("<<<2");
-
next();
-
console.log("2>>>");
-
}
同样,先执行 console.log("<<<2") ,紧接着触发
dispatch(2)
console.log("2>>>") 等待执行
dispatch(2) 运行时
-
const dispatch = (index:2) => {
-
if (index:2 >= middleware.length:3) return Promise.resolve();
-
const fn = middleware[index:2];
-
return Promise.resolve(
-
fn({}, () => dispatch(index:2 1)));
-
};
同理,此时会执行middleware中间函数数组的第3个匿名回调函数
-
(ctx, next) => {
-
console.log("<<<3");
-
next();
-
console.log("3>>>");
-
}
同样,先执行 console.log("<<<3") ,紧接着触发
dispatch(3)
dispatch(3) 运行时
-
const dispatch = (index:2) => {
-
if (index:3 >= middleware.length:3) return Promise.resolve();
-
const fn = middleware[index:2];
-
return Promise.resolve(
-
fn({}, () => dispatch(index:2 1)));
-
};
- 返回Promise.resolve(),
- 相当于fn3的next( )函数执行完毕,开始执行 console.log("3>>>"),
- 此时fn2的next ( )函数执行完毕,开始执行fn2的 console.log("2>>>")
- 此时fn1的next ( )函数执行完毕,开始执行fn2的 console.log("1>>>")
- 至此,所有中间件函数执行完毕
其运行逻辑如下
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhiaechi
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22