express
和koa
的中间件区别就在与express的中间件里,next
函数前不能加 await
,因为next
函数是个同步函数,没有返回promise
,所以如果使用了await
,那么就会导致所期望的异步代码执行顺序和想象的不一样,而koa
的next(也就是dispatch)
包裹了一层Promise.resolve
,await
后就能保证即使有异步操作,洋葱模型也能正常执行.
简单写下大致思路
class App {
constructor() {
this.middleWare = [];
}
use(name, ...middleWares) {
middleWares.forEach((fn) => {
const handler = {
key: name,
handler: fn,
};
this.middleWare.push(handler);
});
}
run(name) {
let index = 0;
// 获取下个符合条件的中间件
const getValidMiddle = () => {
if (index >= this.middleWare.length) return null;
const { key, handler } = this.middleWare[index++];
if (key == name || key == "*") {
return handler;
} else {
return getValidMiddle();
}
};
// next 函数,中间件函数包裹一层, next函数是同步函数,所以 await next()并不会等待中间件的返回结果
// function next() {
// const handler = getValidMiddle();
// if (handler) {
// handler.call(null, { index: `第${index}个中间件` }, next);
// }
// }
// 改造成 返回 promise.resolve的函数,就可以解决
function next() {
const handler = getValidMiddle();
if (handler) {
return Promise.resolve(
handler.call(null, { index: `第${index}个中间件` }, next)
);
}
return Promise.resolve();
}
next();
}
}
const app = new App();
app.use("test", m1, m2);
app.use("test", m3, m4);
app.run("test");
async function m1(args, next) {
console.log("中间件1 开始,参数:>>>");
next && (await next());
console.log("中间件1 结束");
}
async function m2(args, next) {
console.log("中间件2 开始,参数:>>>");
next && (await next());
console.log("中间件2 结束");
}
async function m3(args, next) {
await sleep(2000);
console.log("中间件3 开始,参数:>>>");
next && next();
console.log("中间件3 结束");
}
function m4(args, next) {
console.log("中间件4 开始,参数:>>>");
next && next();
console.log("中间件4 结束");
}
function sleep(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}