chenfei-hnu / Blog

个人整理的跟前端相关的文档 ( This is some front-end development related documentation )
9 stars 2 forks source link

koa1/2原理及实现 #23

Open chenfei-hnu opened 5 years ago

chenfei-hnu commented 5 years ago

koa使用洋葱模型进行中间件调用,执行时类型函数内调用函数形成执行上下文栈依次调用 koa1基于的co库,所以koa1利用Generator来代替回调,而koa2由于node对async/await的支持,所以koa2利用的是async/await 与express相比,洋葱模型后置处理逻辑的编码更加方便,异常处理用try catch更自然,Context 提供更多的node原生功能

koa1

const middleware = [];
const use = (fn) => {
    middleware.push(fn);
}

function* noop() {}

const co = (it) => {
    return new Promise((resolve, reject) => {
        function next(data) {
            let {
                value,
                done
            } = it.next();
            if (!done) {
                if (value.next) {
                    co(value).then(() => {
                        next(data);
                    });
                } else if (value.then) {
                    value.then(() => {
                        next(data);
                    })
                } else {
                    next(data);
                }
            } else {
                resolve(value);
            }
        }
        next();
    });
}
const compose = (middleware) => {
    return function* (next) {
        if (!next) next = noop();
        let index = middleware.length;
        while (index--) {
            next = middleware[index].call(this, next);
        }
        yield* next;
    }
}
use(function* (next) {
    console.log(1);
    yield next;
    console.log(2);
});

const asyncPromise = (timeout) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, timeout)
    });
}
use(function* (next) {
    console.log(3);
    const data = yield asyncPromise(1000);
    yield next;
    console.log(4);
});

use(function* () {
    yield 1;
    console.log(5);

});
const gen = compose(middleware);
co(gen());

koa2

const middleware = [];
const use = (fn) => {
    middleware.push(fn);
}
const asyncPromise = (timeout) => {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, timeout)
    });
}

use(async (ctx, next) => {
    console.log("中间件1 start");
    await asyncPromise(1000);
    await next();
    console.log("中间件1 end");
})
use(async (ctx, next) => {
    console.log("中间件2 start");
    await asyncPromise(1000);
    await next();
    console.log("中间件2 end");
})
use(async (ctx, next) => {
    console.log("中间件3 start");
    await asyncPromise(1000);
    await next();
    console.log("中间件3 end");
})
const compose = (middleware) => {
    return (ctx, response) => {
        const dispatch = (i) => {
            let fn = middleware[i];
            if (i === middleware.length) fn = response;
            if (!fn) return Promise.resolve()
            return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
        }
        return dispatch(0);
    }
}
compose(middleware)(null, () => {
    console.log('success');
});