Compose应用

在用 PHP 开发自己博客后台的时候,碰到一个问题:一般的后台框架都会提供中间件,给使用者自定义额外的功能,so,PHP 应该如何整合一系列的功能中间件呢? 你可能想到的实现 注册中间件的时候将其存到数组中,执行时遍历数组。 const app = new App(); // 注册中间件 // this.middlewares.push(middleware); app.use(middleware); // 使用中间件 // 内部方法实现 function __internal() { // some injections here. for (let i = 0; i < this.middlewares.length; i++) { const fn = this.middlewares[i]; // 你可以往中间件注入一些东西 fn(injection); } } 最简单、最笨的一种方式。来看看其他大佬怎么做的。 redux 中间件 应用了函数式编程盛行的“函数合成(compose)”。简而言之就是,将多个中间件函数合成一个函数,最后只执行一次。 redux 中间件的注册方式是一次性的,无需多次调用 .use 或其它用于注册的函数: applyMiddleware(middleware1, middlwware2, ...); 来看看 applyMiddleware 实现: // applyMiddleware.js // middlewareAPI 是我们注入中间件的一些东西 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // compose 所有中间件,之后一次性调用 dispatch dispatch = compose(...chain)(store.dispatch) compose 在这里不在赘述。想了解的老铁们可以去瞅下 阮一峰的函数式编程入门。 koa 中间件 将注册的中间件存到 middleware 数组中,执行时调用 compose 之后的函数。 // application.js class Application { use(fn) { // 注册时 this.middleware.push(fn); } callback() { // compose 成一个 fn 函数调用 const fn = compose(this.middleware); } } koa-compose 与函数式编程概念中的 compose 略有不同,采用“递归 + Promise”的方式,形成了其独特的“洋葱模型”。 // koa-compose/index.js return dispatch(0); function dispatch(i) { let fn = middleware[i]; // 终止条件 if (!fn) return Promise.resolve(); // 递归调用 // dispatch 就是往中间件注入的 next // 从这里我们也可以看到为什么写中间件时一定要调用 next 方法, // 如果不调用的话,递归不会继续,之后的而中间件也不执行,一直 pending return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } 这样也很难理解为什么 await next() 之后的代码会反序执行,来看个简单的例子: async function foofoo() { console.log(2); await Promise.resolve(); console.log(2); } async function foo() { console.log(1); await foofoo(); console.log(1); } a(); // 1 2 2 1 还是只可意会,不可言传[奸笑]。关键点在于递归。 express 中间件 注册成数组(额外的实例化成 layer)形式,调用时从第一个开始执行,执行完毕后调用 next 找到第二个,第二个执行完后 next 第三个,直至最后。 express 中间件的应用其实是到 router 上面,所有我们直接跳到路由的设计: // router/index.js // 注册时 proto.use = function use(fn) { // 实例化 layer var layer = new Layer(options, fn); this.stack.push(layer); }; // 执行时 proto.handle = function handle(req, res, next) { // 生成引用。要注意 this 这个坑 var stack = this.stack; next(); // 这个 next 就是我们注入了中间件里 function next(err) { while (match !== true && idx < stack.length) { layer = stack[idx ++]; match = matchLayer(layer, path); // 如果你调用 next(1) 的话,这里会一直不匹配! // 最后直接跳过后面的中间件返回 1 if (layerError) { match = false; continue; } } // 找到后 // 其实内部也就是执行我们的 middleware 函数 return layer.handle_request(req, res, next); } }; 跟 for 循环很像,但是又依赖于开发者手动 next 触发下一个。 so ? 讲了半天中间件,和我要的 compose 有半毛钱关系哦。 redux 用到了精髓 koa 有点皮毛东西 express 八竿子打不着 强行 pick 一波:如果你还在古老地循环调用一系列函数,不妨 compose 试下。


JavaScript全屏阅读

下一篇:打酱油

上一篇:钢铁是怎样炼成的

Ctrl + Enter