怎样设计一个好的api?
假设现在有一个需求如下:
要实现一个自动切换的交通信号灯
版本1
var light = document.querySelector('#light')
;(function reset() {
light.className = 'stop'
setTimeout(() => {
light.className = 'wait'
setTimeout(() => {
light.className = 'pass'
setTimeout(reset, 2000)
}, 2000)
}, 2000)
})()
相信很多人都会像方式1这么写,实现简单,思路也清晰,但如果我想交换信号的顺序,就需要修改代码逻辑部分,而且如果要添加多个状态,显然会出现很多的嵌套回调,这样写显然不好
版本2
changeState(light, 'stop', 'wait', 'pass')
function changeState(list, ...state) {
let i = 0
let timer = setInterval(() => {
list.className = state[i++ % state.length]
}, 2000)
}
版本2很好的解决了版本1的问题,而且封装性也比较好,但是这样并不是很优秀
版本3
function poll(...atcs) {
let index = 0
return (...args) => {
let fn = atcs[index++ % atcs.length]
return fn.apply(this, args)
}
}
let setState = function(state) {
light.className = state;
}
let changeState = poll(
setState.bind(null, 'stop'),
setState.bind(null, 'wait'),
setState.bind(null, 'pass')
)
let timer = setInterval(changeState, 2000)
版本4用到了一个高阶函数apply,poll函数设计的十分精妙,完全抽象,复用性极强,但是毕竟需求可能还会变,比如我要不同的时间间隔,我想1s后stop,2s后wait...,怎么办...? 难道我们要回到版本1,当然不行。接下来看版本4
版本4
function wait(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
function setState(state) {
light.className = state
}
let reset = async () => {
// Promise.resolve()
// .then(setState.bind(null, 'stop'))
// .then(wait.bind(null, 1000))
// .then(setState.bind(null, 'wait'))
// .then(wait.bind(null, 1500))
// .then(setState.bind(null, 'pass'))
// .then(wait.bind(null, 2000))
// .then(reset)
setState('stop')
await wait(1000)
setState('wait')
await wait(1500)
setState('pass')
await wait(2000)
reset()
}
reset()
版本4巧妙的将等待这个动作给抽象了出来,解决了时间间隔问题,由此我们可以封装一个动画类
class Animate {
constructor(el, args) {
this.el = el
this.states = args
}
async start() {
let states = this.states
let setState = this.setState
let wait = this.wait
let el = this.el
for(let s of states) {
console.log(s.state)
await new Promise(async resolve => {
setState(el, s.state)
await wait(s.duration)
resolve()
})
}
this.start()
}
wait(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
setState(el, state) {
el.className = state
}
}
const test = new Animate(light, [
{state: 'stop', duration: 1000},
{state: 'wait', duration: 1500},
{state: 'pass', duration: 2000},
])
test.start()
利用这个可以实现一些动画例如这个轮播图