前言
NodeJS这东西是不是学过了,之后感觉又像没学到什么东西???
我最近翻到了之前学习node的笔记,又结合了一些大佬的经验,这里把node系列相关的东西串联一下,分享给小伙伴们,顺便我自己也加深一下印象。
总共分为六篇
node打怪升级系列 - 手写中间件篇
node打怪升级系列 - 手写发布订阅和观察者篇
node打怪升级系列 - 手写compose(洋葱模型)
本文重点记录下洋葱篇
里的装备
正文开始
洋葱效果展示
js
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log("querying start 1");
next();
console.log("querying end 1");
})
app.use(async (ctx, next) => {
console.log("querying start 2");
next();
console.log("querying end 2");
})
app.use(async (ctx, next) => {
console.log("querying start 3");
next();
console.log("querying end 3");
})
const main = ctx => {
ctx.body = "hello world";
}
app.use(main)
app.listen(3008)
分析实现思路
1, app.use收集函数到中间件
js
use(fn) {
// ...
this.middleware.push(fn);
return this;
}
2, app.listen创建服务,并对中间件进行编排处理
js
listen(...args) {
// ...
const server = http.createServer(this.callback());
return server.listen(...args);
}
根据http.createServer(this.callback())
可知,callback函数的返回值是一个带有形参(req,res)的函数
callback的作用
-
对中间件进行编排:
const fn = compose(this.middleware)
-
把ctx传给fn来配合编排的逻辑:
this.handleRequest(ctx, fn)
等于fn(ctx)
js
callback() {
const fn = compose(this.middleware);
// ...
const handleRequest = (req, res) => {
// 把req res等核心信息注入到ctx中
const ctx = this.createContext(req, res);
// ...
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
2.1, 对中间件进行编排(核心:洋葱模型
)
js
function compose(middleware) {
// ...
/**
* @param {Object} context
* @return {Promise}
* @api public
*/
return function (context, next) {
// last called middleware #
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)
}
}
}
}
洋葱模型的核心在于dispatch()
函数,是一个递归逻辑
拿洋葱效果展示
区域的代码举例,使用了三次app.use()
后,中间件数组middleware
成员为[fn1, fn2, fn3]
-
第一次执行 dispatch(0) -> middleware[0] -> fn1(context, dispatch(1)) -> 打印 console.log("querying start 1");
-
第二次执行 dispatch(1) -> middleware[1] -> fn2(context, dispatch(2)) -> 打印 console.log("querying start 2");
-
第三次执行 dispatch(2) -> middleware[2] -> fn3(context, dispatch(3)) -> 打印 console.log("querying start 3");
-
第四次执行 dispatch(3) -> middleware[3] -> fn3没有 -> return Promise.resolve()
-
接下来执行各个函数中之前未执行完成的代码
-
打印console.log("querying end 3");
-
打印console.log("querying end 2");
-
打印console.log("querying end 1");
对了,顺便再多说一句 fn1(context, dispatch(1))为例
实际上,等于执行了
js
app.use(async (ctx, next) => {
console.log("querying start 1");
next();
console.log("querying end 1");
})
中的
js
(ctx, next) => {
console.log("querying start 1");
next();
console.log("querying end 1");
})
context
等于ctx
, dispatch(x)
等于 next()
2.2, 把ctx传给fn来配合编排的逻辑
js
handleRequest(ctx, fnMiddleware) {
// ...
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
完结
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。