简介
不少复杂函数中都存在一个问题,逻辑步骤或分支较多,很多时候我们都采用if else/Promise等来处理,而有些场景我们不得不额外把一部分逻辑封装成一个函数待以重用。
这些问题便会使得整个函数变得更复杂和难以维护,借助Promise.then思路我们可以模拟一个步骤控制器step来解决此问题
调用示例:
step(next => {
// some condition
next()
})
.step(next => {
// do something
})
step函数体可实现异步/同步效果,这取决于next的执行时机.
准备
首先,step是一个函数,并且可以反复的调用自身:
function step(excuter) {
if(!excuter || typeof excuter !== 'function') {
return false
}
// do something...
return {
step(excuter) {
return this
}
}
}
在函数执行后返回一个对象,包含step方法,step方法执行后又返回这个对象本身,从而达到重复调用
生成步骤队列
在每一步执行时,把任务本身放入任务队列中,供下一步使用
function step(excuter) {
if(!excuter || typeof excuter !== 'function') {
return false
}
const steps = []
return {
step(excuter) {
steps.push(excuter)
return this
}
}
}
执行步骤
执行步骤:第一步通过微任务异步执行,后面的步骤可同步可异步,取决于上一步执行完成的时机.
注意:由于生成步骤队列需要优先生成,故此第一步只能设为异步执行.
function step(excuter) {
if(!excuter || typeof excuter !== 'function') {
return false
}
const steps = []
const next = (data) => {
if(!steps.length) {
return false
}
const stepExceter = steps.shift()
stepExceter(next, context)
}
window.queueMicrotask(() => {
excuter(next, context)
})
return {
step(excuter) {
steps.push(excuter)
return this
}
}
}
步骤数据
每个步骤都是一个独立函数,不利于变量传递,因此在执行的时候常常需要储存或取数.
const context = {
data: new Map(),
prevData: null,
}
context.setData = (key, value) => {
if(context.data.has(key)) {
throw new Error(`key: ${key}, 已存在!`)
}
context.data.set(key, value)
}
context.getData = (key) => {
return context.data.get(key)
}
完整代码
// util.function.js
export function step(excuter) {
if(!excuter || typeof excuter !== 'function') {
return false
}
const context = {
data: new Map(),
prevData: null,
}
const steps = []
const next = (data) => {
if(!steps.length) {
return false
}
context.prevData = data
const stepExceter = steps.shift()
stepExceter(next, context)
}
context.setData = (key, value) => {
if(context.data.has(key)) {
throw new Error(`key: ${key}, 已存在!`)
}
context.data.set(key, value)
}
context.getData = (key) => {
return context.data.get(key)
}
window.queueMicrotask(() => {
excuter(next, context)
})
return {
step(excuter) {
steps.push(excuter)
return this
}
}
}
export default {
step
}
调用样例
runAction(item, tab) {
const {action,validateInput} = item
const constFunction = [
'copyResult', 'clearResult', 'clearAll'
]
try {
if(constFunction.includes(action)) {
return this[action](tab)
}
step(next => {
if(!action) {
throw new Error('该动作不是一个函数,请检查配置.')
}
next()
})
.step(next => {
// 需要校验
if(validateInput && tab.inputs.length > 1) {
this.$refs[tab.name][0].validate().then(() => {
next()
})
} else {
next()
}
})
.step(next => {
const params = tab.inputs.map(x => {
let value = tab.inputModel[x.name]
return x.transform === 'json' ? JSON.parse(value) : value
})
let result = action.apply(this, params)
if (result instanceof Promise) {
result.then(res => {
next(res)
})
} else {
next(result)
}
})
.step((next, ctx) => {
this.updateOutputResult(tab.output, ctx.prevData)
})
} catch (error) {
ElMessage.error(error.message);
}
}