「✍ koa」随便写

112 阅读1分钟

本来想直接读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 , 但不同的是,42的打印并没有等到拿到数据后才执行,并且请求的响应也失败了

原因:一次请求会在所有中间件执行完之后默认结束,但上述代码中,我们没有手动阻塞fn(ctx, next)的执行,当42打印完后,本次请求也就结束了,拿到数据时的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..