You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
use(fn){if(typeoffn!=='function')thrownewTypeError('middleware must be a function!')debug('use %s',fn._name||fn.name||'-')this.middleware.push(fn)returnthis}
// 省略部分注释和部分代码functioncompose(middleware){// 判断传递是否为数组,且每个数组都必须为函数,否则抛出异常if(!Array.isArray(middleware))thrownewTypeError("Middleware stack must be an array!");for(constfnofmiddleware){if(typeoffn!=="function")thrownewTypeError("Middleware must be composed of functions!");}// 这里默认返回一个函数,handleRequest函数会调用这个返回的函数,并且传递 contextreturnfunction(context,next){letindex=-1;// 默认情况下执行一次 dispatch ,dispatch因为是函数声明所以会提升到做作用域顶部returndispatch(0);functiondispatch(i){// 通常情况下不会遇到,但是如果执行两次就会抛出异常,例如第一次调用index为-1,i为0,第二次执行则变成index为0,i也为0则抛出错误if(i<=index)returnPromise.reject(newError("next() called multiple times"));index=i;letfn=middleware[i];// 这里从koa的实现可以看到,是没有传递next的,所以这行代码可以跳过if(i===middleware.length)fn=next;// 执行到最后一项的时候直接返回不再继续递归下去if(!fn)returnPromise.resolve();try{// 这里实现很巧妙,利用了bind的原理,bind的第一个参数为this,之后的参数为函数的预设值,最后返回一个函数// 然后根据Promise.resolve的实现规范,如果传递的Promise.resolve是一个Promise要等待新的Promise执行完成之后决定状态// 这里推荐看下PromiseA+规范实现returnPromise.resolve(fn(context,dispatch.bind(null,i+1)));}catch(err){returnPromise.reject(err);}}};}
经常在使用 koa 的时候,通过
.use
的形式来注册各种中间件,例如下面一段代码这里会输出 1,3,4,2,下面就来翻看一下源码看看这个中间件实现的具体原理。
在看具体代码之前,先温习一下,使用 koa 的最小运行代码是什么样的
可以看到,最后通过 listen 方法来启动服务,那我们重点先看下 use 和 listen 做了什么事情。
use
代码比较少,这里直接贴上去了,use 的主要作用就是给 middleware 添加相对应的 fn。
listen
这里 server 是 http 的库的方法,我们先不管,主要看一下 this.callback 做了什么事情。
handleRequest 这个函数的实现如下
可以看到,最终是把 this.compose 返回的 fn 进行了调用,那么由此可以知道 this.compose 就是具体中间件调度的具体实现。
compose
this.compose 可以在 constructor 中看到,默认情况下就是 koa-compose,这个库也非常精简只有 50 行代码,下面会通过注释的形式来对源码进行一个说明。
ok,这里基本上就讲完了,还是对照最初的示例来看
这里传递给 compose([fn1, fn2]),之后 i 为 0,返回
之后 i 为 1 的时候
此时 i 为 2,发现数组取不到值了,执行
回到最最后一步,还没有解释为什么会洋葱结构这样来执行代码
这里我们知道先执行第一个函数
await next()
会执行 next,而从源码dispatch.bind(null, i + 1)
可以知道下一项就是第二个函数await next()
从源码知道 i 为 2 的时候,返回的是if (!fn) return Promise.resolve();
上面可能有点绕,但是其实 koa-compose 利用了事件循环的机制,对于微任务每次执行都会放到微任务队列,等待主线程执行栈调用,而栈的特点就是先进后出,所以这也是为什么会输出 1,3,4,2 的原因了。
最后
如果文章有说的不对地方欢迎指出,最后本人正在找工作,有相关 hc 岗位欢迎滴滴。
The text was updated successfully, but these errors were encountered: