使多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
看定义,这不就是koa么。
开放-封闭原则:对扩展是开放的,对修改是封闭的。
链式调用
Function.prototype.next=function(fn){
var self=this
return function(){
var res=self.apply(this,arguments)
if(res==='nextSuccessor'){ // 这个可以自己定义,表示当前函数处理不了,得移交给下一环进行
return fn.apply(this,arguments)
}
return res
}
}
var chain=f1.next(f2).next(f3)
小结
作者也说,
降低发请求的对象和处理请求对象间的耦合性
可见在js中,职责链模式基本上是用来处理服务端通信的问题,而这种模式已经被现有的框架,如koa封装好了。
补充:koa洋葱圈模型
我翻来覆去的看,这个职责链模式就是koa的中间件机制。但是koa是对异步任务的特化处理,表现为洋葱圈模型,相比之下,express中的事件机制不存在这种先进后出的性质,各个中间件不会被下一个中间件阻塞。
koa的核心思想就是缓存中间件,并用compose函数来将中间件串联起来。
koa中通过use函数来挂载中间件,中间件函数都是async await函数,(感觉本质上是promise链形式)
const Koa = require("koa");
const app = new Koa();
app.use(async function (ctx, next) {
console.log(">> one");
await next();
console.log("<< one");
});
app.use(async function (ctx, next) {
console.log(">> two");
await next();
ctx.body = "two";
console.log("<< two");
});
app.use(async function (ctx, next) {
console.log(">> three");
await next();
console.log("<< three");
});
app.listen(8080);
/**
* 访问浏览器, 我们可以看到
* >> one
* >> two
* >> three
* << three
* << two
* << one
* /
洋葱圈模型实际上指的是一个http请求以next函数为界限,经过中间件两次。行为可以参考栈机制,最先挂载上的中间件最后执行完毕。
// use函数的实现,功能上看只有将中间件放入队列的作用
use (fn) {
// ...
this.middleware.push(fn) // use 函数把我们的中间件加入到 middleware 的数组中
return this
}
callback () {
const fn = compose(this.middleware) // 把 middleware 数组传入到 compose 函数, 得到一个新的函数.
if (!this.listenerCount('error')) this.on('error', this.onerror)
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
return handleRequest
}
handleRequest (ctx, fnMiddleware) {
const res = ctx.res
res.statusCode = 404
const onerror = err => ctx.onerror(err)
const handleResponse = () => respond(ctx)
onFinished(res, onerror)
return fnMiddleware(ctx).then(handleResponse).catch(onerror) // 在 handleRequest 函数中, 直接就是把 ctx 对象传入到 fn 中进行执行.
}
compose函数是整个koa的核心,它将各个中间件串联起来
function compose (middleware) {
return function (context, next) {
// 记录上一次调用的中间件
let index = -1
return dispatch(0)
function dispatch (i) {
index = i // 赋值中间件索引
let fn = middleware[i]
if (i === middleware.length) fn = next
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) // 把下一个中间件的索引通过 bind 绑定,这个就是next
// 而且这一步还对dispatch进行了递归调用,也是这一步使得中间件串联起来
} catch (err) {
return Promise.reject(err)
}
}
}
}
next应该是框架传入的队列中下一个函数,compose函数最终返回一个串联好中间件的函数,程序最终运行的也应该是这个函数。
只能有空再多看看了,现在功力感觉还不够。。。