内容简介:上一篇讲了Koa 中间件采取的是中间件洋葱模型,具体原理可见当然实际上更复杂,还要考虑中间件的异步执行和中间件如何进行嵌套。Koa 中异步处理在 Koa1 中使用的是 generator + co.js,在 Koa2 中使用的是 async/await,我们本次采用 async/await 来处理异步。中间件的嵌套可以通过将中间件当参数传递来实现嵌套。据此我们对上面的代码进行进一步加工:
上一篇讲了 如何编写属于自己的 Koa 中间件 ,本篇将根据原理实现一个简单的中间件处理函数,并对 Koa 中间件处理函数 compose 函数进行源码解析。
1. compose 函数简单实现
Koa 中间件采取的是中间件洋葱模型,具体原理可见 如何编写属于自己的 Koa 中间件 。本质就是将中间件嵌套执行:
function middleware0(){ console.log('middleware0') } function middleware1(){ console.log('middleware1') } // 将两个中间件嵌套执行 middleware0(middleware1())
当然实际上更复杂,还要考虑中间件的异步执行和中间件如何进行嵌套。Koa 中异步处理在 Koa1 中使用的是 generator + co.js,在 Koa2 中使用的是 async/await,我们本次采用 async/await 来处理异步。中间件的嵌套可以通过将中间件当参数传递来实现嵌套。据此我们对上面的代码进行进一步加工:
ps:Node7.6+ 支持 async/await
async function middleware0(next){ console.log('middleware0') await next() } async function middleware1(next){ console.log('middleware1') } // 将两个中间件嵌套执行 middleware0(middleware1)
Koa 中通过 compose 函数对中间件的进行处理。compose 函数参数为 middleware 的数组, middleware 数组成员是通过 use 方法添加的中间件。下面写个简单的 compose 函数,来实现多个中间件的处理:
async function middleware0(next){ console.log('middleware0') await next() console.log('middleware0 end') } async function middleware1(next){ console.log('middleware1') await next() console.log('middleware1 end') } async function middleware2(next){ console.log('middleware2') await next() console.log('middleware2 end') } /** * @param {Array} 中间件数组 */ function compose (middleware) { // 从第一个中间件开始执行 return dispatch(0) function dispatch(i){ // 获取第 i 个中间件 fn = middleware[i] // 获取不到中间件,则直接返回结束 if(!fn) return // 执行第 i 个中间件,并传入第 i + 1 个中间件 return fn(() => dispatch(i + 1)) } } // 执行 compose([middleware0, middleware1, middleware2])
2. 源码解析
我们已经简单实现了一个 compose 函数,现在来看下 Koa 中源码的实现。Koa 中的 compose 函数已经提取到 koa-compose 包中,其中的核心代码如下:
/** * @param {Array} 中间件数组 * @return {Function} */ 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 function (context, next) { let index = -1 // 从第一个中间件开始执行 return dispatch(0) function dispatch (i) { // 同一个中间件多次调用 next 抛出异常 if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i // 获取第 i 个中间件 let fn = middleware[i] /** * 中间件执行结束,检查是否有传入 next 回调函数 * 此 next 并非中间件执行的 next 参数 */ if (i === middleware.length) fn = next /** * 所有的返回都是Promise对象 * Promise对象可以保证中间件和返回请求对象之间的执行顺序 */ if (!fn) return Promise.resolve() try { // 执行第 i 个中间件,并传入第 i + 1 个中间件 return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } }
通过解析可以发现,源码相对于我们的实现更加健全:
-
更完善的异常处理
- 在执行前统一对传入参数进行检查
- 多次执行 next 函数抛出异常处理等
- 最终返回结果 Promise 化,保证中间件和整个处理函数在 Koa 中的执行顺序,具体可参考下面 Koa 源码片段:
/** * application.js * fnMiddleware(ctx) 就是 compose 函数返回的函数,默认不传入 next 参数 * Promise 保证中间件,handleResponse 执行顺序。 */ fnMiddleware(ctx).then(handleResponse).catch(onerror)
3. 小结
从最开始的编写 Koa 中间件,到现在阅读 compose 函数源码,Koa 中间件机制并不复杂,了解之后,我们可以运用、编写更合适的中间件,构建自己的 Koa 应用。
- 本文首发于公众号,更多内容欢迎关注我的公众号: 阿夸漫谈
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Laravel HTTP——路由中间件的别名解析与排序源码解析
- 消息中间件 RocketMQ 源码解析 —— 调试环境搭建
- redux, koa, express 中间件实现对比解析
- Koa2 中间件原理解析 —— 看了就会写
- 中间件(WAS、WMQ)运维 9个常见难点解析
- Laravel HTTP——SubstituteBindings 中间件的使用与源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Go Web 编程
[新加坡]Sau Sheong Chang(郑兆雄) / 黄健宏 / 人民邮电出版社 / 2017-11-22 / 79
《Go Web 编程》原名《Go Web Programming》,原书由新加坡开发者郑兆雄(Sau Sheong Chang)创作、 Manning 出版社出版,人名邮电出版社引进了该书的中文版权,并将其交由黄健宏进行翻译。 《Go Web 编程》一书围绕一个网络论坛 作为例子,教授读者如何使用请求处理器、多路复用器、模板引擎、存储系统等核心组件去构建一个 Go Web 应用,然后在该应用......一起来看看 《Go Web 编程》 这本书的介绍吧!