compose函数的作用就是组合函数,依次组合传入的函数,控制函数的执行顺序
function compose (middleware) {//判断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!')}//返回一个匿名函数return function (next) {let index = -1return dispatch(0)function dispatch (i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))//根据下文可知index是从-1递增index = ilet fn = middleware[i]//如果函数数组被遍历完,并且i===middleware.length,此时fn 应该为undefined;之后把next函数赋值给fnif (i === middleware.length) fn = next//结束递归条件,返回promiseif (!fn) return Promise.resolve()try {/*实现递归,执行fn,并返回promise,这里需要注意与常用的递归实现方法不同,dispatch函数对自身的调用是通过对fn的传参,这样通过递归实现了对middleware的遍历,fn的执行顺序就如下图的洋葱模型一样,这也是koa的经典模型*/return Promise.resolve(fn(dispatch.bind(null, i + 1)));} catch (err) {return Promise.reject(err)}}}}
测试:
let fun1 = function (next) {console.log(1)/*执行传入的next函数,next在这里相当于compose里的dispatch函数,执行next就相当于dispatch对自身的调用,从而可以继续执行middleware当中剩下的fn函数。由于返回的是promise,所以可以异步等待next的执行,*/next()/*next执行完毕才会打印后面的内容,这里并不会直接打印'next1',而是等待后边的函数先执行*/console.log('next1')}let fun2 = function (next) {console.log(2)next()console.log('next2')}let fun3 = function (next) {console.log(3)/*由于fun3是middleware数组当中的最后一个函数,所以传入的next函数为compose返回的匿名函数的参数,在这里为say函数,所以会在3和next3当中打印1000*/next()console.log('next3')}let say = function () {console.log(1000)}compose([fun1, fun2, fun3])(say)
打印结果 1,2,3,1000,next3,next2,next1
参考源码地址:https://github.com/koajs/compose/blob/master/index.js

