koa被认为是下一代的web框架,其最大的特点就是独特的中间件控制,是一个典型的对洋葱模型的实现。其中koa和koa2的中间件实现思路是一样的,只是具体实现有所区别,koa2在node7.6之后使用
async/await
替代generator
函数,本文以async
/await
来实现洋葱模型。
洋葱模型
借来一张网上的图片来说明下,洋葱模型主要分两个过程——“出”和“入”,对于“入”方向先处理的中间件,在“出”方向时就会变成后处理,这样的好处是同一个中间件可以同时处理两个过程,而且不用在意顺序。在koa中,前置中间件就可以处理入方向的req请求和出方向的res结果,其处理逻辑类似于python平台中的django框架的中间件。
代码实现
实现思路就是运用async/await函数,因为await关键字支持promise,可以在promise没有返回的时候暂停执行后面的代码。
基于这个原理,如果传入的一组函数都是async函数,并且以某种手段让后面的函数作为前一个函数的await表达式,那么我们不就可以实现洋葱模型了么?
const md1 = async (next) => {
console.log('gen1 start')
await next()
console.log('gen1 end')
}
const md2 = async (next) => {
console.log('gen2 start')
await next()
console.log('gen2 end')
}
const md3 = async (next) => {
console.log('gen3 start')
await next()
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('gen3 end')
resolve()
}, 2000)
})
}
const stack = [md1, md2, md3]
const noop = async () => {
console.log('noop')
}
const call = () => {
let index = -1
function dispatch (i) {
index = i
let fn = stack[i]
if (i === stack.length) fn = noop
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(() => dispatch(i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
return dispatch(0)
}
call().then(() => {
console.log('stack end')
}).catch(err => {
console.log(err)
})