咱们在不少地方都用到了中间件的概念,我理解的中间件就是代码执行过程当中插入一些中间过程。git
就是普通函数的中间件,函数的嵌套执行github
/** 执行中间件,获得中间件的返回函数数组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)) }
模拟这个过程express
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的中间件相似这个原理redux
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();
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) } } }
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方法,执行迭代器。promise
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) + '"')); } }); }