阅读背景:
- 已经读过上一篇koa1的文章
- async await
- class
还是跟前面koa1文章类似,先看看koa2的使用方法;
koa2使用方法
|
|
koa2原理
从上面代码可以看出,koa2中间件函数使用了异步函数、关键字async await;
使用async与await带来了什么呢?
async await class
熟悉async与await的童鞋一定会知道这是JS最新处理异步的关键字;
async会把正常函数转化为async函数,执行后会返回promise;async 函数中可能会有 await 表达式,这将会使 async 函数暂停执行,等待 Promise 正常解决后继续执行 async 函数并返回解决结果。
async/await的目的是简化同步使用 promises 的做法,并对一组Promises执行一些操作。正如Promises类似于结构化回调,async/await类似于组合生成器和 promises。
先看看koa现在主体代码:
从上面看出现在koa主体代码使用了最新es6中的class、解构与展开. 这样带来一个限制,只能使用new关键字来生成实例,不然报错.
app.callback
下面是callback源码以及req来临的处理代码:
|
|
上面代码与koa1最大不同的是中间件函数的处理,之前是co.wrap(compose(this.middleware)), 现在是compose(this.middleware); 这个到最后执行的关键函数是什么,怎么调用参数来执行;
compose
先看看现在的compose代码:
|
|
中间件处理之后生成函数其实就是
koa2执行流程
- 请求到来
- 执行fnMiddleware(ctx), 也就是上面的compose处理中间件得到的函数
- 执行dispatch(0)
- 其实就是执行中间件数组第一个中间件,直到await next()停止;
- 而next实际就是传递给第一个函数的第二个参数
function next() {return dispatch(i+1)}
- 执行next函数,也就是执行dispatch(1), 从而进入到第二个中间件;
- 依次往复,直到执行到最后一个中间件next
- 当执行dispatch(n+1)时,n+1取到fn为空,则
return Promise.resolve()
, 接着执行最后一个中间件await后面的内容, 并返回一个resolve的Promise - 然后返回到上一个中间件,执行await后面的内容,依次返回, 直到第一个中间件返回resolve了的promise
- 最后执行
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
语句then后面的handleResponse - 至此整个请求执行完所有中间件,并返回结果;
dispatch注意点
dispatch函数中有个需要注意的地方:
第一句if (i <= index) return Promise.reject(new Error('next() called multiple times'))
这个异常什么时候会执行呢, 当一个中间件中有多次await next()的时候,由于执行某一个中间件的时候根据闭包原理会存储一个i值,而多个await意味着多次执行next函数
|
|
第一次执行上面的函数返回后index就会等于i+1,当第二次执行到dispatch(i+1)里面,i值没变,就会得到i+1 === index
, 报错。这也是koa1与koa2的不同点,也是解决在上一篇koa1文章结尾的问题.
总结
至此,就完成了koa2原理的核心部分,其余部分都是小变化。
也可以看出await与async简化了koa的实现,并且代码比koa1中的多次封装中间件、co函数的协助、委托生成器与迭代器好理解多了.