使多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

看定义,这不就是koa么。
开放-封闭原则:对扩展是开放的,对修改是封闭的。

链式调用

  1. Function.prototype.next=function(fn){
  2. var self=this
  3. return function(){
  4. var res=self.apply(this,arguments)
  5. if(res==='nextSuccessor'){ // 这个可以自己定义,表示当前函数处理不了,得移交给下一环进行
  6. return fn.apply(this,arguments)
  7. }
  8. return res
  9. }
  10. }
  11. var chain=f1.next(f2).next(f3)

小结

作者也说,

降低发请求的对象和处理请求对象间的耦合性

可见在js中,职责链模式基本上是用来处理服务端通信的问题,而这种模式已经被现有的框架,如koa封装好了。

补充:koa洋葱圈模型

我翻来覆去的看,这个职责链模式就是koa的中间件机制。但是koa是对异步任务的特化处理,表现为洋葱圈模型,相比之下,express中的事件机制不存在这种先进后出的性质,各个中间件不会被下一个中间件阻塞。
koa的核心思想就是缓存中间件,并用compose函数来将中间件串联起来
koa中通过use函数来挂载中间件,中间件函数都是async await函数,(感觉本质上是promise链形式)

  1. const Koa = require("koa");
  2. const app = new Koa();
  3. app.use(async function (ctx, next) {
  4. console.log(">> one");
  5. await next();
  6. console.log("<< one");
  7. });
  8. app.use(async function (ctx, next) {
  9. console.log(">> two");
  10. await next();
  11. ctx.body = "two";
  12. console.log("<< two");
  13. });
  14. app.use(async function (ctx, next) {
  15. console.log(">> three");
  16. await next();
  17. console.log("<< three");
  18. });
  19. app.listen(8080);
  20. /**
  21. * 访问浏览器, 我们可以看到
  22. * >> one
  23. * >> two
  24. * >> three
  25. * << three
  26. * << two
  27. * << one
  28. * /

洋葱圈模型实际上指的是一个http请求以next函数为界限,经过中间件两次。行为可以参考栈机制,最先挂载上的中间件最后执行完毕。

  1. // use函数的实现,功能上看只有将中间件放入队列的作用
  2. use (fn) {
  3. // ...
  4. this.middleware.push(fn) // use 函数把我们的中间件加入到 middleware 的数组中
  5. return this
  6. }
  7. callback () {
  8. const fn = compose(this.middleware) // 把 middleware 数组传入到 compose 函数, 得到一个新的函数.
  9. if (!this.listenerCount('error')) this.on('error', this.onerror)
  10. const handleRequest = (req, res) => {
  11. const ctx = this.createContext(req, res)
  12. return this.handleRequest(ctx, fn)
  13. }
  14. return handleRequest
  15. }
  16. handleRequest (ctx, fnMiddleware) {
  17. const res = ctx.res
  18. res.statusCode = 404
  19. const onerror = err => ctx.onerror(err)
  20. const handleResponse = () => respond(ctx)
  21. onFinished(res, onerror)
  22. return fnMiddleware(ctx).then(handleResponse).catch(onerror) // 在 handleRequest 函数中, 直接就是把 ctx 对象传入到 fn 中进行执行.
  23. }

compose函数是整个koa的核心,它将各个中间件串联起来

  1. function compose (middleware) {
  2. return function (context, next) {
  3. // 记录上一次调用的中间件
  4. let index = -1
  5. return dispatch(0)
  6. function dispatch (i) {
  7. index = i // 赋值中间件索引
  8. let fn = middleware[i]
  9. if (i === middleware.length) fn = next
  10. try {
  11. return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) // 把下一个中间件的索引通过 bind 绑定,这个就是next
  12. // 而且这一步还对dispatch进行了递归调用,也是这一步使得中间件串联起来
  13. } catch (err) {
  14. return Promise.reject(err)
  15. }
  16. }
  17. }
  18. }

next应该是框架传入的队列中下一个函数,compose函数最终返回一个串联好中间件的函数,程序最终运行的也应该是这个函数。
只能有空再多看看了,现在功力感觉还不够。。。