compose函数的终极解决方案
大约 2 分钟
compose函数的终极解决方案
首先
compose函数是什么?
compose
就是执行一系列的任务(函数),比如有以下任务队列(数组里都是函数)
let tasks = [step1, step2, step3, step4]
每一个step
都是一个步骤,按照步骤一步一步的执行到结尾,这就是一个compose
, compose
在函数式编程中是一个很重要的工具函数,在这里实现的compose
有三点说明
- 第一个函数是多元的(接受多个参数),后面的函数都是单元的(接受一个参数)
- 执行顺序的自右向左的
- 所有函数的执行都是同步的
普通版本
function compose(...fns) {
return function composed(result){
// 拷贝一份保存函数的数组
var list = fns.slice();
while (list.length > 0) {
// 将最后一个函数从列表尾部拿出
// 并执行它
result = list.pop()( result );
}
return result;
};
}
异步版本
const composePromise =
(...fns) =>
(...args) =>
fns.reverse().reduce(
(pre, next) =>
pre.then((...arg) => {
return next.call(null, ...arg)
}),
Promise.resolve(...args)
)
const composePromiseFn = [
async (arg) => {
console.log(1, arg)
return arg
},
async (arg) => {
console.log(2, arg)
return arg
},
async (arg) => {
console.log(3, arg)
return arg
},
async (arg) => {
console.log(4, arg)
return arg
},
]
const callPromise = composePromise(...composePromiseFn)
callPromise('helloPromise')
reduce 版本
const composeRight =
(...fns) =>
(...arg) =>
fns.reduceRight((arg, fn) => fn(...arg), arg)
const compose = (...fns) =>
fns.reduce(
(pre, next) =>
(...arg) =>
pre(next(...arg))
)
洋葱模型
function compose(middleware) {
if (!Array.isArray(middle)) throw new TypeError('type error')
for (let i of middleware) {
if (typeof i !== 'function') throw new TypeError('中间件的类型必须为函数')
}
// 记录当前中间件的执行位置 通过闭包保存
let index = -1
function dispatch(i) {
index = i
// 取出中间件
let fn = middleware[index]
// 如果是最后一个中间件就执行回调
if (i === middleware.length || !fn) return Promise.resolve()
try {
return Promise.resolve(fn(null, dispatch.bind(null, i + 1)))
/* .catch(err => {
console.log(err)
}) */
} catch (error) {
return Promise.reject(error)
}
}
return dispatch(0)
}
// 1 3 5 6 4 2
const middle = [async (context, next) => {
console.log(1)
await next()
console.log(2)
}, async (context, next) => {
console.log(3)
await next()
console.log(4)
}, async (context, next) => {
console.log(5)
// await next()
throw '错误'
console.log(6)
}]
compose(middle).then(() => {
console.log('完成')
}).catch(err => {
console.log(err)
})