本来想直接读koa的源码,但是耐不住性子,自己还是喜欢在使用的过程中一点点的凿
洋葱模型
当我们第一次使用koa,大概会写下这些代码
const koa = require("koa");
const app = new koa();
app.use(async (ctx, next) => {
console.log('1')
await next();
console.log('2')
});
app.use(async (ctx, next) => {
console.log('3')
await next()
console.log('4')
});
app.listen(3020, () => {
console.log("listening in 3020");
});
官方告诉你,根据洋葱模型的设计,你会发现打印结果为
1 => 3 => 4 => 2
(这里没有做路由,简单的接收所有请求)
现在加上一些异步操作
先模拟一个获取数据的异步方法( http/db之类的操作)
const getData = () => {
return new Promise((resolve) => {
setTimeout(() => resolve("ok"), 1500);
});
};
app.use(async (ctx, next) => {
console.log('1')
await next();
console.log('2')
});
app.use(async (ctx, next) => {
console.log("3");
const res = await getData();
ctx.response.body = res; // 设置响应
await next();
console.log("4");
});
app.listen(3020, () => {
console.log("listening in 3020");
});
打印结果没问题,依然是
1 => 3 => 4 => 2
这时我无聊的改了一下写法
app.use(async (ctx, next) => {
console.log('1')
await next();
console.log('2')
});
app.use(async (ctx, next) => {
console.log("3");
let fn = async (ctx, next) => {
const res = await getData();
console.log(res);
ctx.response.body = res;
};
fn(ctx, next);
console.log("4");
});
app.listen(3020, () => {
console.log("listening in 3020");
});
会发现打印依然是 1 => 3 => 4 => 2 , 但不同的是,4和2的打印并没有等到拿到数据后才执行,并且请求的响应也失败了
原因:一次请求会在所有中间件执行完之后默认结束,但上述代码中,我们没有手动阻塞fn(ctx, next)的执行,当4和2打印完后,本次请求也就结束了,拿到数据时的ctx.response.body = res已经无效了
解决:对fn(ctx, next)进行阻塞
app.use(async (ctx, next) => {
console.log('1')
await next();
console.log('2')
});
app.use(async (ctx, next) => {
console.log("3");
let fn = async (ctx, next) => {
const res = await getData();
console.log(res);
ctx.response.body = res;
};
await fn(ctx, next);
console.log("4");
});
app.listen(3020, () => {
console.log("listening in 3020");
});
源码
todo..