async/await
Koa2 中间件使用了 async/await 语法糖,所以搞懂以下这个例子,就能轻松搞懂了。
以这个例子为例,模仿中间件的写法,写一个 test 函数。里面的 fn 是异步函数,里面再次写上 await next1,next1 里再写 next2。
function test() {
try {
const fn = async() => {
console.log('use1');
await next1();
console.log('use1-end');
}
return Promise.resolve(fn());
} catch (error) {
return Promise.reject(error)
}
}
test()
async function next1() {
try {
const fn = async() => {
console.log('use2');
await next2();
console.log('use2-end');
}
return Promise.resolve(fn());
} catch (error) {
return Promise.reject(error)
}
}
async function next2() {
try {
const fn = async() => {
console.log('use3');
console.log('use3-end');
}
return Promise.resolve(fn1());
} catch (error) {
return Promise.reject(error)
}
}
输出:
Promise 改写 async/await
假如,把 await 换成 Promise 的写法:
function test() {
const fn = async() => {
console.log('use1');
// await next1();
new Promise((resolve, reject) => {
const fn = async() => {
console.log('use2');
// await next2();
new Promise((resolve) => {
try {
const fn = async() => {
console.log('use3');
console.log('use3-end');
}
return resolve(Promise.resolve(fn()));
} catch (error) {
return reject(Promise.reject(error))
}
}).then((res) => {
console.log('use2-end');
})
}
return resolve(Promise.resolve(fn()));
}).then(() => {
console.log('use1-end');
})
}
return Promise.resolve(fn())
}
test()
async function next1() {
const fn = async() => {
console.log('use2');
await next2();
console.log('use2-end');
}
return Promise.resolve(fn());
}
async function next2() {
const fn = async() => {
console.log('use3');
console.log('use3-end');
}
return Promise.resolve(fn());
}
输出也是:
可以发现,async/await 改成 Promise 后,Promise 里面嵌套了 Promise,同时可以看出如果很多中间件的话,书写会复杂化。
简化上面 Promise 写法:
new Promise((resolve, reject) => {
console.log('use1');
new Promise((resolve) => {
const fn = () => console.log('use2');
return resolve(Promise.resolve(fn())); // Promise 需等 resolve执行后,才执行 then
}).then(() => {
console.log('use2-end');
})
return resolve(Promise.resolve(fn()));
}).then(() => {
console.log('use1-end');
})
理解这一点很重要,这是理解 Koa2 中间件原理的关键:Promise 需等 resolve 执行后,才执行 then,也就是说,只要 resolve 了,就可以执行 then。而 resolve 之前,都要等待。
所以就能理解先是里层的 Promise resolve 之后,再到外层的 Promise resolve。
Koa2 原理
理解了上面的那个例子,那么理解 Koa2 原理就简单了。核心是在 compose 函数里,这里通过递增 i 的方式,不断地往下执行 dispatch(i)。
const http = require('http')
class Application {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn); // 把中间件添加到栈中
}
createContext(req, res) {
const ctx = {}
return ctx;
}
compose(middlewares) {
return function(ctx) {
const dispatch = (i) => {
try {
const fn = middlewares[i];
// const next = () => dispatch(i + 1);
return Promise.resolve(fn(ctx, () => dispatch(i + 1))); // 递增 i,这是外部调用的 next
} catch (error) {
return Promise.reject(error);
}
}
dispatch(0);
}
}
handleRequest(req, res) {
const ctx = this.createContext(req, res); // 处理上下文
const fn = this.compose(this.middlewares); // 处理中间件
return fn(ctx); // 开始执行
}
listen() {
const server = http.createServer(this.handleRequest.bind(this));// 防止在 handleRequest 执行时拿到的 this 指向 server
server.listen(...arguments);
}
}
module.exports = Application
所谓洋葱圈模型,即:
在
koa中,中间件被next()方法分成了两部分。next()方法上面部分会先执行,下面部门会在后续中间件执行全部结束之后再执行。
把上面的 aysnc/await 执行先后顺序搞懂了,也就搞懂了 Koa2 洋葱圈模型。