koa2
是一个轻量的Node.js
http
框架。
koa2 采用
async
和await
来处理异步,koa2 实例的 use 函数的参数都是中间件。
先来看一个 koa2 的核心小 demo
// 中间件的仓库
const arr = [
async next => {
console.log(1);
await next();
console.log(2);
},
async next => {
console.log(3);
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(console.log(4));
}, 1000);
}); // 异步操作 await 会等待后面的promise resolve 后再向下执行
await next();
console.log(5);
},
async next => {
console.log(6);
},
async next => {
// 不会执行 因为上一个函数中没有执行next
console.log(7);
await next();
console.log(8);
},
async next => {
// 不会执行 因为前面的函数中没有执行next
console.log(9);
},
];
function fun(arr) {
function dispose(index) {
const currentFun = arr[index];
const next = dispose.bind(null, index + 1);
return currentFun(next); // 尾递归
}
dispose(0);
}
fun(arr); // 先打印 1 3 一秒后打印4 6 5 2
code 开始 (新建一个 ware.js 文件)
const http = require("http");
const urlParser = require("url"); // 解析url字符串和url对象
class Middleware {
constructor() {
this.wares = []; // 存储中间件
}
use(fun) {
this.wares.push(fun); // 收集中间件
return this;
}
/* 中间件处理的核心 */
handleMiddleware(wareList) {
return ctx => {
// 中间件调用
const dispose = index => {
const currentFun = wareList[index];
return new Promise((resolve, reject) => {
try {
// 使用Promise.resolve 包装 currentFun 防止外部传入的currentFun为一个普通函数
/* dispose.bind(null, index + 1)就是next 让dispose继续执行下一个中间件
如果没有在中间件中调用dispose.bind(null, index + 1) 则不会再去获取下一个中间件
*/
return resolve(currentFun(ctx, dispose.bind(null, index + 1)));
} catch (e) {
return Promise.reject(e);
}
});
};
// 立即执行一下仓库的第一个中间件
dispose(0);
};
}
createContext(req, res) {
const { method, url } = req;
const { query } = urlParser.parse(url, true);
// ... 这里远比这个复杂, 我们只做一个简单的包装
return {
method,
url,
query,
res,
};
}
serverHandle() {
return (req, res) => {
// 当请求来的时候我们去触发中间件
const fn = this.handleMiddleware(this.wares);
// 得到当前请求的上下文对象
const ctx = this.createContext(req, res);
fn(ctx);
};
}
listen(...args) {
const app = http.createServer(this.serverHandle()); // 这里只是为了模拟得到一个http服务
app.listen(...args); // 直接交给node原生的http模块处理
}
}
module.exports = Middleware;
测试 (同一目录下新建一个 demo.js 文件)
const Demo = require("./ware");
const app = new Demo();
app.use(async (ctx, next) => {
await next();
console.log(`${ctx.method} ${ctx.url}`);
});
app.use(async ctx => {
ctx.res.end("hello world");
});
app.listen(5000, () => {
console.log("http://localhost:5000");
});
Usage
node demo.js
浏览器访问 http://localhost:5000
=> hello world
。