koa2 总体流程原理浅析(二) 之 中间件原理
koa 的中间件机制巧妙的运用了闭包和
async await
的特点,形成了一个洋葱式的流程,和 JS 的事件流 (捕获 -> target -> 冒泡) 相似handleRequest(ctx, fnMiddleware) { const res = ctx.res; res.statusCode = 404; const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror); }上述代码是 request 事件的句柄,也就是说每一个请求到来,都会执行这个总方法
- onerror 为请求设置了错误处理的方法
- handleResponse 是当中间件完成后给浏览器返回 response 的方法,里面是原生的
res.end(body)
- onFinished 是判断请求最终有没有完成,根据不同的结果采取不同的策略
fnMiddleware(ctx)
就是执行所有中间件函数,然后返回一个 Promise 对象,不出错的话执行handleResponse
洋葱式的中间件
值得一提的是,中间件原理的代码并没有放在 koa 中,而是单独打了一个模块,叫做 ==koa-compose==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 // 返回给 next() if (!fn) return Promise.resolve() try { // 返回给 next(),最外一层返回给 fnMiddleware(ctx).then(handleResponse) return Promise.resolve(fn(context, function next () { // 返回给外一层 fn 的 await return dispatch(i + 1) })) } catch (err) { return Promise.reject(err) } } }
- 执行一次 dispatch 就是执行一个中间件,算是洋葱的一层
- 每个 dispatch 都会返回一个 Promise.resolve 给外面一层的 await(除了第一次,他返回给的是
fnMiddleware(ctx).then(handleResponse)
) - 每个 dispatch 都有一个自己的序号,也就是参数 i (他用闭包控制住了) ,从 0 开始
- 闭包里有一个
index
,是记录执行过的中间件数量。一旦有序号大于数量,说明有中间件执行了两次await next
,这是不被允许的 - 每一层用 Promise.resolve 包裹是因为 await 需要接收一个 Promise 对象
function dispatch(0){ // 第一层的序号 return Promise.resolve(async function a0(){ cnosole.log('0-0') await 111(function next0(){ return (function dispatch(1){ // 第二层的序号 return Promise.resolve(async function a1(){ cnosole.log('1-0') await 222(function next1(){ return (function dispatch(2){ // 第三层的序号 return Promise.resolve(async function a2(){ cnosole.log('2-0') await 333(function next2(){ return (function dispatch(3){ // i == middleware.length ,算是洋葱芯吧 // fn[3] == undefined,说明中间件已经到洋葱的最里面了,开始向外返回 return Promise.resolve() })() })()333 console.log('2-1') }) })() })()222 console.log('1-1') }) })() })()111 console.log('0-1') }) } dispatch(0).then(handleResponse)
思考
1. 普通函数采用 dispatch 算法也能取得洋葱式的流程,为何要使用 async ?
app.use(async function (ctx,next) { console.log('1-1') await new Promise(function(resolve, reject){ setTimeout(function () { console.info ("wait for 10 mini seconds."); resolve(); },10); }); console.log('1-2') next(); console.log('1-3') }) app.use(async function (ctx,next) { console.log('2-1') await new Promise(function(resolve){ setTimeout(function () { console.info ("wait for 10 mini seconds"); resolve(); },10); }); console.log('2-2') next(); console.log('2-3') })试试 next() 前面加上 await 和不加 await 的区别就明白了
2. 为何要用 Promise.resolve 返回
因为他是洋葱式的层级,如果用普通的 Boolean 返回的话,只能返回到上一层,没法全局获取,对错误的把控难以控制。Promise 任何一层报错,都能用 catch 捕获总结
koa 是一个非常轻量级的框架,只实现了中间件处理流程和对res、req
对象的封装。其他的功能都由外部中间件提供。代码不是很多,但是很精妙,对于代码能力的提高有不小的帮助END
相关推荐
boneix 2020-10-21
MrQuinn 2020-08-16
starzhangkiss 2020-04-19
LorenLiu 2020-03-28
Qimingweikun 2020-03-05
发条戏子 2020-02-01
jackyhungvip 2020-01-19
Qimingweikun 2019-12-09
Anything0 2019-11-17
lert0 2019-11-04
88530198 2019-11-03
sqliang 2019-11-03
苹果咏 2019-07-23
bobbaobao 2018-11-13
lert0 2019-07-01
fanix 2019-07-01
liwenbocsu 2019-07-01