koa-compose源码分析

koa-compose是koa中间件的核心部分, 控制着中间件的执行流程, 造就了经典的洋葱模型。
module.exports = compose​

function compose(middleware) {
    //首先是参数类型检查,不符合就抛错
    //middleware必须是数组
    if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
        //middleware的项必须是函数
    for (const fn of middleware) {
        if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
    }​

    // 返回一个闭包, 保持对 middleware 的引用
    return function(context, next) {
        let index = -1
        //从中间件第一项开始,执行中间件函数
        return dispatch(0)

        function dispatch(i) {
            //索引小于等于index,说明一个中间件函数中next被调用多次,不允许
            if (i <= index) return Promise.reject(new Error('next() called multiple times'))
            //index更新为i,对应前面的检测
            index = i

            //取出中间件中的第i个函数
            let fn = middleware[i]
            //如果索引到数组最后,函数变成next,不明白???
            if (i === middleware.length) fn = next
            //遍历结束,fn为undefined,返回Promise,方便后面thenable
            if (!fn) return Promise.resolve()
            try {
                //关键部分,fn中传入context和next函数,对应于app.use((ctx,next) => {...})的用法
                //Promise.resolve让返回值支持thenable调用
                return Promise.resolve(fn(context, function next() {
                    //尾递归dispatch,索引加1,不断调用中间件的下一个函数,直至用尽
                    //尾递归可以提升效率,缩短call Stack
                    //递归是洋葱模型的关键,递归本身的call Stack就是按照洋葱模型来执行
                    //next之前执行代码,在next时候调用下一个中间件,下一个中间件继续执行next之前代码,next执行下下个中间件
                    //等到递归完成,到最后一个中间件,开始出栈,先执行最里面的next之后的代码,以此往外执行,形成洋葱调用
                    return dispatch(i + 1)
                }))
            } 
            catch (err) {
                //如果递归调用中抛错,就reject
                return Promise.reject(err)
            }
        }
    }
}

 

posted @ 2020-05-09 14:45  全玉  阅读(407)  评论(0编辑  收藏  举报