首先我们看下中间件的运作机制,当一个被加入到中间件pipe列表中的中间件开始处理传入的内容时,它需要调用一个next,通过next来触发下一个中间件,如果没有下一个,则表示所有中间件已经处理完毕,进入正常的处理程序。而如果没有执行next函数,则进程被block住,处于一直等待状态,这样,我们就可以在中间件中进行异步处理,在异步处理的回调中去执行next函数。
那么,怎能在js中实现这种机制呢?我们来看下超简洁的代码实现:
/** * @desc process with a pipe line * @param array middlewares: a middleware is a function which like `function deal(args, next, stop) {}`, * in this function, you can modify args and use `next(args)` to pass it to next middleware, * next middleware will recieve `next(args)`'s `args` as first parameter, * or use `stop()` to stop process * @param any params, will be used as the first middleware's first parameter. * @return a promise */ export default function crossPipeLine(params, middlewares) { return new Promise((resolve, reject) => { let i = 0; // if middlewares is not as expected if (!Array.isArray(middlewares)) { middlewares = typeof middlewares === "function" ? [middlewares] : []; } // remove no use middlewares middlewares = middlewares.filter((item) => !!item); let roll = (args) => { let pipe = middlewares[i]; if (!pipe) { resolve(args); return; } i++; return new Promise((next, stop) => pipe(args || params, next, stop)).then(roll).catch(reject); }; roll(params); }); }
上面这段代码,创建了一个通道处理器,在处理器中执行中间件函数,全部执行完之后,返回一个promise。看下一个中间件函数怎么写:
function myMiddleware(req, next, stop) {
// modify req
next(req)
}
其中,红色的next和stop是两个固定参数,都是函数,放在middleware函数的最后两个参数位置,前面的req是自定义参数,且只有这一个自定义参数(数据类型可以根据自己的情况传入),比如myMiddleware([1, 2, 3], next)或者myMiddleware({ a: 1 }, next),stop是可以省略的,但是next不可以省略,因为如果不执行next或stop中的任何一个,那么程序会停滞在这里,不会继续往下执行。
next的参数是传给下一个中间件函数的第一个参数。比如上面这里的next(req),那么在下一个中间件myMiddleware2(req, next, stop)里面的req就来自于next。在一些情况下next可以不传参数,比如你在中间件里面直接修改了req,由于req是个对象,是引用型数据,所以在下一个中间件里面,你使用req时,已经是被修改过的。这种情况下,可以不给next传参,不传参的情况,实际上是把当前中间件接收到的第一个参数传给下一个中间件,但如果你想传递字符串或数字或boolean,就必须要通过next传入。
接下来,看下如何在一个项目中如何使用这个通道处理器:
let myMiddlewares = [ myMiddleware ] let newReq = await crossPipeLine(req, myMiddlewares)
这样就实现了一个非常简洁的中间件机制。
2017-12-16 4593