使多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
看定义,这不就是koa么。
开放-封闭原则:对扩展是开放的,对修改是封闭的。
链式调用
Function.prototype.next=function(fn){var self=thisreturn 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.resres.statusCode = 404const 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 = -1return dispatch(0)function dispatch (i) {index = i // 赋值中间件索引let fn = middleware[i]if (i === middleware.length) fn = nexttry {return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) // 把下一个中间件的索引通过 bind 绑定,这个就是next// 而且这一步还对dispatch进行了递归调用,也是这一步使得中间件串联起来} catch (err) {return Promise.reject(err)}}}}
next应该是框架传入的队列中下一个函数,compose函数最终返回一个串联好中间件的函数,程序最终运行的也应该是这个函数。
只能有空再多看看了,现在功力感觉还不够。。。
