洋葱模型是一种很经典的程序设计思路:
function compose (middleware) {
// some code
}
复制代码
它能接受一个函数数组,然后返回一个新的函数,达到这样的效果:
let middleware = []
middleware.push((next) => {
console.log(0)
next()
console.log(3)
})
middleware.push((next) => {
console.log(1)
next()
console.log(1.1)
})
middleware.push(() => {
console.log(2)
})
let fn = compose(middleware)
fn()
/*
0
1
2
1.1
3
*/
复制代码
当我们尝试执行fn的时候,它会按照顺序调用之前函数数组中的函数,并且给每一个小函数传递一个参数: next函数。
如果在小函数中执行next,就会调用这个函数的下一个函数,如果没有执行next,程序就不会往下走。所以你可以看到上面的打印顺序。
但是问题来了,我们怎么写这个compose函数?
首先, 这个compose函数肯定会返回一个大的函数嘛,所以:
function compose (middleware) {
return function () {
}
}
复制代码
然后返回的函数是fn,如果我们尝试执行fn, fn将会尝试执行middleware[0],同时就想我们之前说的,它会给这个小函数传入一个next函数
function compose (middleware) {
return function () {
let f1 = middleware[0]
f1(function next(){
})
}
}
复制代码
然后这个next函数有开关的功能,如果我们执行next函数,next函数就会调用数组中下一个函数,所以
function compose (middleware) {
return function () {
let f1 = middleware[0]
f1(function next(){
let f2 = middleware[1]
f2(function next(){
...
})
})
}
}
复制代码
然后。。。这是递归!为了让上面的更优雅,我们可以这样写
function compose (middleware) {
return function () {
dispatch(0)
function dispatch (i) {
const fn = middleware[i]
if (!fn) return null
fn(function next () {
dispatch(i + 1)
})
}
}
}
复制代码
通过一个dispatch来优雅的实现递增。
然后我们还希望这个函数支持异步函数:
function compose (middleware) {
return async function () {
await dispatch(0)
function async dispatch (i) {
const fn = middleware[i]
if (!fn) return null
await fn(function next () {
dispatch(i + 1)
})
}
}
}
复制代码
最后一步,允许用户传递参数
function compose (middleware) {
return async function () {
let args = arguments
await dispatch(0)
function async dispatch (i) {
const fn = middleware[i]
if (!fn) return null
await fn(function next () {
dispatch(i + 1)
}, ...args)
}
}
}
复制代码
参考: koa-compose
源代码: github