# koa 洋葱模型

当我们调用 app.use()会push 中间件到中间件数组,

当我们调用 app.listen()的时候,内部会调用this.callback()

this.callback又调用了compose函数,将中间件进行组合,

当遇到路由的时候,会调用 handleRequest函数,该函数调用 compose函数返回的函数,

该函数(调用 compose函数返回的函数)通过递归的方式,调用中间件。

执行第一个中间件的时候,通过递归的方式将下个中间件传入,当我们调用await next()的时候,会进入下一个中间件, 当最后一个中间件调用 return Promise.resolve(199) 的时候回跳出当前中间件,回到上一个中间件 这就是洋葱模型

# 应用demo


const koa = require('koa')
const Router = require('koa-router')
const app = new koa()
const router = Router()
app.use(async (ctx, next) => {
console.log('diyige')
    // console.log('start')
    await next();
    // const rt = ctx.response.get('X-Response-Time');
    // console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
// x-response-time
app.use(async (ctx, next) => {
    console.log('dierge')
    // const start = Date.now();`
    await next();
    // const ms = Date.now() - start;
    // ctx.set('X-Response-Time', `${ms}ms`);
});
function sleep(){
    return new Promise((resolve) =>{
    setTimeout(() => {
        resolve('ok')
    }, 3000);
})
}
router.get('/', async(ctx, next) => {
  const res = await sleep()
  ctx.body = res
})

app.use(router.routes())

app.listen(3000)

console.log('server listening at port 3000')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 代码翻译


var middleware = [async function(ctx,next){
    console.log(1)
    await next()
    console.log(5)
    return ctx
},async function(ctx,next){
    console.log(2)
    await next()
    console.log(4)
    return ctx
},async function(ctx,next){
    await next()
    return ctx

},async function(ctx,next){
    ctx.body = {
        msg:'请求成功',
        code: 200
    }
}];
var context ={
    name:1,
    age:2
}

function compose (middleware) {
    return function (context, next) {
      // last called middleware #
      let index = -1
      return dispatch(0)
      function dispatch (i) {
        if (i <= index) return Promise.reject(new Error('next() called multiple times'))
        index = i
        let fn = middleware[i]
        if (i === middleware.length) fn = next
        if (!fn) return Promise.resolve()
        try {
          return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
}
const fnMiddleware = compose(middleware);
fnMiddleware(context).then(res =>{
    console.log('res :', res);
})

// fnMiddleware 执行后 展开形式如下
function fnMiddleware(ctx){
    return Promise.resolve((async function(ctx){
        console.log(1)
        await Promise.resolve((async function(ctx){
            console.log(2)
            await Promise.resolve((async function(ctx){
                console.log(3)
                return Promise.resolve(199) 
            })())
            console.log(4)
        })())
        console.log(5)
    })())
}
fnMiddleware({}).then(res =>{
    console.log('res',res)
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69