Koajs中间件之next(二)
Koajs中间件之定义(一)
Koajs中间件之next(二)
Koajs中间件之context(三)
Koajs中间件之next(二)
Koajs中间件之context(三)
第一篇文章中我们讲过,“在Koa中,中间件是指连贯整个 Koa 应用程序,并共享资源的独立插件”,注意两个词,“连贯”与“共享资源”,与上面的代码一一对应,“连贯”对应“next”,“共享资源对应context”。
Koa 中通过 next 贯穿整个应用程序,下面分析一下 next 中做了什么。
中间件集合
Koa 类中的构造函数中初始化了一堆数据,其中两个重要的,一个是“middleware”,另一个是“context”。(非关键代码使用...省略)
constructor() { ... this.middleware = []; this.context = Object.create(context); ... }
所有的中间件在一个数组存放,当我们调用“app.use”方法的时候会往数组中加入我们自定义的中间价
use(fn) { ... this.middleware.push(fn); return this; }
最后通过“koa-compose”来统一触发中间件队列
callback() { const fn = compose(this.middleware); ... return (req, res) => { ... fn(ctx).then(() => respond(ctx)).catch(ctx.onerror); }; }
koa-compose
koa-compose 源码只有短短几十行,关键代码不到10行,直接贴上源码
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, function next () { return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } } }
如果我们需要使用 Koa 的洋葱模型可以直接调用 koa-componse 来达到目的
const koaCompose = require('koa-compose'); const middleware1 = (ctx, next) => { console.log('middleware1 >>>>>'); next(); console.log('middleware1 <<<<<'); } const middleware2 = (ctx, next) => { console.log('middleware2 >>>>>'); next(); console.log('middleware2 <<<<<'); } const middleware3 = (ctx, next) => { console.log('middleware3 >>>>>'); console.warn(ctx); next(); console.log('middleware3 <<<<<'); } const fn = koaCompose([middleware1, middleware2, middleware3]); fn({ a: 'a' }, (ctx) => { console.warn(ctx); console.warn('The last next use do =======<'); return ctx; }).then((ctx) => { console.warn(ctx); console.warn('end =====<'); });
输出:
middleware1 >>>>> middleware2 >>>>> middleware3 >>>>> { a: 'a' } { a: 'a' } The last next use do =======< middleware3 <<<<< middleware2 <<<<< middleware1 <<<<< undefined end =====<
精简 koa-componse
为了更好的分析代码,去除 koa-componse 代码中的各种非关键判断及异步处理逻辑后,代码如下
const compose = function (middlewares) { // 返回一个函数,提供两个参数,一个是传入的上下文,另一个是所有中间件执行完毕后回调 return function(context, last) { let idx = -1; // 初始定义当前执行中间件下标未-1,即表示当前未执行任何中间件 return dispatch(0); // 手动触发第1个中间件 function dispatch(i) { idx = i; // 设置当前执行中间件下标 let fn = middlewares[i] || last; try { // 执行当前中间件的时候,给当前中间件参数中的next参数赋值为一个函数,在这个函数中执行下一个中间件 if (fn) fn(context, function() { dispatch(i + 1); // 触发下一个中间价,并且将中间件执行下标+1 }) } catch (err) { // 所有的中间件执行完毕,执行最后回调 last(context); } } } };
执行代码:
const run = compose([middleware1, middleware2, middleware3]); run({ a: 'a' }, () => { console.warn('Middlewares do last =======<'); });
输出:
middleware1 >>>>> middleware2 >>>>> middleware3 >>>>> Middlewares do last =======< middleware3 <<<<< middleware2 <<<<< middleware1 <<<<<
总体思路
- 将所有中间件推送到一个数组中
- 第一个中间件首先执行
- 在第一个中间件执行前,将第一个中间件中的 next 参数设置为一个触发下一个中间件执行的函数
- 第一个中间件调用 next 函数,把执行器交给下一个中间价
- 循环往复,直最后一个中间件执行完毕
- 所有中间件执行完毕后,依次执行next外层代码
优点
- 解决多重回调地狱模式
- 统一处理上下文context挂载与传递
- 异常捕获
缺点
- 当一个项目中存在多个中间件时,对于性能会有一定影响,对于优化来说是一种考验
参考资料
相关推荐
ericxieforever 2020-09-03
earthhouge 2020-08-15
Andrewjdw 2020-07-26
pengkunstone 2020-07-19
风吹夏天 2020-07-18
amazingbo 2020-06-28
QCkiss 2020-06-04
FightFourEggs 2020-06-02
czsay 2020-06-01
troysps 2020-05-30
hanyujianke 2020-05-05
idning 2020-04-29
dbhllnr 2020-04-26
freedomfanye 2020-04-19
RocNg 2020-04-18
shawsun 2020-03-23
assastor 2020-03-20
AndroidAmelia 2020-03-01