中间件的实现对比
我们在很多地方都用到了中间件的概念,我理解的中间件就是代码执行过程中插入一些中间过程。
1 redux中的中间件
就是普通函数的中间件,函数的嵌套执行
/**
执行中间件,得到中间件的返回函数数组chain,然后利用compose方法,生成嵌套的执行chain
方法的包装dispatch函数,
中间件的形式是
(getState, dispatch)=> next => action => {
next(action);
}
*/
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
/**
store.dispatch 就是第一个next 是last ware的next
(...args) => {
return ware0(ware1(ware2(last(...args))))
}
dispatch = ware0(ware1(ware2(last(...args))))
所以中间件中next传入后返回的函数就是我们需要的函数形式,
例如dispatch 需要的函数形式是 传一个action
*/
return {
...store,
dispatch
}
}
}
/**
reduceRight是数组的从右至左执行,
初始的参数是最后一个函数接受dispatch,
的到的一个action=>{
dispatch(action);
}
形式的函数,作为参数composed
f的形式是
next=>action=>{
}
最终形成的就是
(...args) => {
return funcs0(funcs1(funcs2(last(...args))))
}
*/
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
模拟这个过程
function func1 (next) {
return function () {
console.log("func1 start");
next();
console.log("func1 end");
}
}
function func2 (next) {
return function () {
console.log("func2 start");
next();
console.log("func2 end");
}
}
function func3 (next) {
return function () {
console.log("func3 start");
next();
console.log("func3 end");
}
}
function App () {
this.middlewares = []
}
App.prototype.use = function (middleware) {
this.middlewares.push(middleware)
}
App.prototype.exec = function (data) {
//funclist func1(func2(func3(dispatch)))
let funclist = this.middlewares.reduceRight(function (generate, next) {
return next(generate)
}, dispatch)
funclist()
}
function dispatch () {
console.log('dispatch')
}
let app = new App();
app.use(func1)
app.use(func2)
app.use(func3)
app.exec('exec');
模拟直接的中间件过程,express的中间件类似这个原理
function func4 (next) {
console.log("func4 start");
next();
console.log("func4 end");
}
function func5 (next) {
console.log("func5 start");
next();
console.log("func5 end");
}
function func6 (next) {
console.log("func6 start");
next();
console.log("func6 end");
}
let middlewareList = [func4, func5, func6];
function exec() {
let result = middlewareList.reduceRight(function (first, second){
return function (name) {
second(first)
}
},function(){console.log('last next')})
result()
}
exec();
2 koa2的中间件
koa2中的中间件是async函数
也是用use方法添加到数组中,使用的时候重点是compose,如何组装中间件,然后返回Promise
module.exports = class Application extends Emitter {
constructor() {
this.middleware = [];
}
listen() {
debug('listen');
const server = http.createServer(this.callback());
return server.listen.apply(server, arguments);
}
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
callback() {
const fn = compose(this.middleware);
if (!this.listeners('error').length) this.on('error', this.onerror);
const handleRequest = (req, res) => {
res.statusCode = 404;
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
}
};
//////koa-compose
这里从第一个中间件开始执行,传给中间件的next,就是执行下一个中间件的方法,并且还可以返回值,这里利用了async函数的返回值是promise,并且可以await promise,
等待执行,达到了写代码和同步类似的效果。
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0) / //从第一个中间件开始执行,async函数的返回值也是promise。
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)
}
}
}
3 koa1的中间件
koa1的中间件是generator函数,所以关键点是对中间件的compose方法不同,koa2中利用的async函数可以直接执行返回promise,而koa1中generator函数不能直接执行,所以
koa1中的compose是把generator生成的迭代器进行传递,所以next是迭代器,koa2中next是方法,然后生成一个大的generator函数,利用co模块进行执行。
co模块的执行,是对next拿到的value先进行promise化,然后再执行promise,在promise的then和catch中继续调用迭代器的next方法,执行迭代器。
app.callback = function(){
if (this.experimental) {
console.error('Experimental ES7 Async Function support is deprecated. Please look into Koa v2 as the middleware signature has changed.')
}
//经过compose和co的wrap得到一个promise
var fn = this.experimental
? compose_es7(this.middleware)
: co.wrap(compose(this.middleware));
var self = this;
return function handleRequest(req, res){
res.statusCode = 404;
var ctx = self.createContext(req, res);
onFinished(res, ctx.onerror);
fn.call(ctx).then(function handleResponse() {
respond.call(ctx);
}).catch(ctx.onerror);
}
};
/*****************koa-compose*****************/
function compose(middleware){
return function *(next){
if (!next) next = noop();
var i = middleware.length;
while (i--) {
next = middleware[i].call(this, next);
}
return yield *next;
}
}
function *noop(){}
/*****************co模块*****************/
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
//继续执行迭代器
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
//如果迭代器执行完毕
if (ret.done) return resolve(ret.value);
//promise化value
var value = toPromise.call(ctx, ret.value);
//执行value
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}
如果觉得有收获请关注微信公众号 前端良文 每周都会分享前端开发中的干货知识点。