koa-compose源码解读

koa

koajs:next generation web framework for node.js。

Koa是基于Node.js的下一代web开发框架。它是基于中间件机制的优雅、简洁、表达力强、自由度高的简单好用的Web框架。通过将各种独立功能的中间件进行自由组合实现具有特定功能的web应用。如通过koa-router中间件实现路由功能、通过koa-cors实现跨域功能等等。在这些中间件中,有一个中间件比较特殊koa-compose,也正是这个中间件的使用才使得在koa中有了我们熟知的洋葱模型。

洋葱模型

洋葱模型是koa中间件的串行控制流程。
洋葱模型

洋葱模型

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  console.log('enter first middleware');
  await next();
  console.log('out first middleware');
});
app.use(async (ctx, next) => {
  console.log('enter second middleware');
  await next();
  console.log('out second middleware');
});
app.use(async (ctx, next) => {
  console.log('enter third middleware');
  await next();
  console.log('out third middleware');
});

app.listen(3000);

输出如下:
洋葱输出
如何让中间件以洋葱模型的方式运行,就是koa-compose的实现的功能。

koa-compose 源码

如上面的代码所示,use的中间件入参:ctxnext,其中ctxkoa中的上下文对象,那么next又是什么呢?带着这个问题我们阅读一下koa-compose的源码。

'use strict'

/**
 * Expose compositor.
 */

module.exports = compose

/**
 * Compose `middleware` returning
 * a fully valid middleware comprised
 * of all those which are passed.
 *
 * @param {Array} middleware
 * @return {Function}
 * @api public
 */

function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  /**
   * @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)
      }
    }
  }
}

koa-compose入参是中间件数组,return的是一个匿名的中间件函数。

function compose (middleware) {
	// 类型检查入参必须是数组
	if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
	// middleware数组内必须中中间件函数
  	for (const fn of middleware) {
    	if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }
  // 返回一个匿名中间件函数
  return funxtion (ctx, next) {}
}

具体看一下return的这个匿名函数是什么:

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)
     }
   }
}

去掉判断条件,看一下最里面具体是什么

return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));

这也就让我们看到了next函数到底是什么,是dispatch.bind(null, i + 1)。也正是通过dispatch将控制权移交给了下一个中间件。在useawait next()正式将控制权移交给下一个中间件,第一个 => 第二个 => ... => 最后一个,当最后一个中间件执行完毕时,此时,开始执行栈将当前栈顶执行环境出栈,最后一个 => 倒数第二个 => ... =>第一个。也就形成了洋葱模型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值