缕一缕koa中间件

vscode 调试用的运行环境是了Launch Program!!!其他环境就错误,虽然还不懂这块,先挖坑吧,累死我了。。。┭┮﹏┭┮

使用中间件

首先使用npm init -y 初始化项目,根目录下新建app.js入口文件,再npm install koa

app.use()里面一次装载中间件,设计中间件功能是在后台打印字符。通过打印出来的字符可以观察到请求再中间件内部的运动轨迹,就和洋葱模型一样。

中间件是回调函数,该函数传入两个参数 ctx, next
ctx 对象是对原生node.js接口的二级封装
当该中间件业务完成时或者需要等待后面的中间件处理业务时,这时候用 next() 调用下一个中间件。

//app.js
const Koa = require('koa')
const app = new Koa()

app.use((ctx, next) => {
    console.log('进入了第一个中间件');
    next()
    console.log('离开了第一个中间件');
})
app.use((ctx, next) => {
    console.log('进入了第二个中间件');
    next()
    console.log('离开了第二个中间件');
})
app.use((ctx, next) => {
    console.log('进入了第三个中间件');
    next()
    console.log('离开了第三个中间件');
})
app.listen(3000)

启动后台服务,node app。再打开postman

在这里插入图片描述

后台打印:

在这里插入图片描述

回头看koa的洋葱模型图:执行next()的过程很像函数调用的过程。在函数内部调用函数,会一直进入函数的内部作用域。当最内部的函数执行完毕后会依次退出。

在这里插入图片描述

而中间件最亮点是处理异步操作的。

const Koa = require('koa')
const app = new Koa()

function waitPromise(time) {
    return new Promise(resolve => {
        setTimeout(resolve, time)
    })
}
app.use(async(ctx, next) => {
    console.log('进入了第一个中间件');
    await waitPromise(1000)
    await next()
    console.log('离开了第一个中间件');
})
app.use(async(ctx, next) => {
    console.log('进入了第二个中间件');
    await waitPromise(5000)
    await next()
    console.log('离开了第二个中间件');
})
app.use(async(ctx, next) => {
    console.log('进入了第三个中间件');
    await waitPromise(2000)
    await next()
    console.log('离开了第三个中间件');
})
app.listen(3000)

写一个函数waitPromise,该函数返回一个promise对象,当到达指定时间后promise才可以完成。
修改三个中间件函数,在里面用waitPromise函数模拟异步操作,同时使用async,await语法在waitPromise函数内部的异步逻辑完成之前进行挂起等待,并且在离开当前中间件之前,挂起等待直到中间件完成业务逻辑。

重新去启动服务并且请求接口,可以看到执行顺序仍然符合洋葱模型,并且是在指定时间后完成的。

深入了解洋葱模型

思考以下问题:

  1. 在 app.use()中传入 async 函数就实现了了中间件,那么 app.use() 到底做了什么
  2. 在中间件函数内调用 await next() 进入下一个中间件,并等待它的promise完成,那么next到底是什么,为什么执行next就可以进入下一个中间件

从入口函数进行调试。用vscode的调试工具打断点行调试。

在这里插入图片描述

实例化koa的时候,实际上是在调用koa:application构造函数
this.middleware = []; 为存放中间件函数的数组,

在这里插入图片描述

接着继续执行,跳入第二个断点,app.use是koa:application的一个方法,首先确保中间件是一个函数,再将中间件函数push到定义的middleware

在这里插入图片描述

最后到下一个断点 app.listen,在 postman 里发出一个请求,继续运行程序,跳入到 listen 的代码块。
要知道 koa 只是基于 nodejs 之上的一个框架,所以无论 koa 做了什么,都由下层的 nodejs 去实现。

在这里插入图片描述

思考我们是如何去启动一个 nodejs 的应用的,是去使用 http.createServer(this.callback()) 启动一个应用。
我们处理的业务全都在 this.callback() 中,所以我们这里看到 koa 去调用 http.createServer(this.callback()) 启动一个 nodejs 后台。
而核心就在于 this.callback() 做了什么?
callback() 返回了一个 handleRequest 函数,这个函数包括了res和req两个参数。这与我们预期是符合的,因为http.createServe是会提供这两个对象的。在handleRequest 函数中会执行handleRequest 函数,并且传入两个参数:ctx,fn。
ctx: 从代码 const ctx = this.createContext(req, res);可以直观的了解到ctx就是基于nodejs提供的req和res的封装。
fn: 从代码 const fn = compose(this.middleware);看出handleRequest 仅仅就是给compose后的fn 传入一个ctx,然后执行一下。

在这里插入图片描述
看compose函数,在\node_modules\koa-compose\index.js

if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

首先确保 middleware 是个函数数组,然后返回一个新的函数,这个函数是我们写的三个中间件的合体。实际上它做的事情就只有一件,就是去执行 dispatch(0)
在dispatch() 函数中,我们看到在返回值当中调用了中间件函数,给我们传入的的第二个参数next就是dispatch,只不过将他的参数绑定为i+1,这是为了让我们执行await next()时,能够执行到当前中间件的下一个中间件函数。

在这里插入图片描述

我们在 return dispatch(0) 打上断点。启动调试,并在postman上发出请求。此时vscode左上角查看变量,在闭包中可以看到middleware数组中就是app.use中传入的三个中间件函数

在这里插入图片描述

然后使用单步调试进入dispatch函数,在函数的开始,通过判断闭包中的index 和 i 值大小来辨别我们是否非法的在同一个中间件中调用了两次 dispatch 函数 。因为我们可以看到 index和 i 是有绑定赋值关系的,在给下个中间件传递dispatch函数时,程序将dispatch的参数绑定到了当前 i+1 ,这意味着当你从调用的中间件返回时,此时的index >= i ,如果在当前中间件继续执行 dispatch 就会报错。

在这里插入图片描述

运行到这一步,以及可以看到fn 已经从middleware 中取了出来,

在这里插入图片描述

继续执行,经过跳出操作进入到第一个中间件内部,执行到next后我们又重回到dispatch进行以上操作,直到 middleware数组被取完,fn:undefined

在这里插入图片描述

所以最后会返回一个空的Promise.resolve()

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值