前言
彦祖们,在我们日常开发中经常会遇到这样一个场景
轮询某个异步条件是否满足,如果满足再去执行某个函数
业务场景复现
笔者现在模拟一个业务场景
有一个叫马宝
的大师,他的技能有 vue
react
node
闪电五连鞭
,他目前的等级是1
js
const user = {
name:'马宝',
skill:['vue','react','node','闪电五连鞭'],
level:1
}
随着时间的推移,马宝
的闪电五连鞭技能
在不断增长,他的等级也在不断提升
当 level
达到 100
的时候将会获得一个称谓 浑元形意太极拳掌门人
模拟服务端逻辑
彦祖们,这段逻辑主要是模拟了真实的业务逻辑,假设每过 10ms
level+1
js
const serverLogic = setInterval(()=>{
user.level++
},10)
业务功能实现
常规写法
我们需要用一个轮询器去监听马宝
是不是达到了 100 级
如果达到了,我们就去给他加上一个称谓,直接上代码
js
const frontLogic = setInterval(()=>{
if(user.level >= 100){
clearInterval(frontLogic)
clearInterval(serverLogic)
user.title = '浑元形意太极拳掌门人'
}
console.log(user)
},10)
看下打印,符合我们的预期
抽象逻辑
这种写法很不通用
假设在开发中有多个这样的场景,彦祖们就需要写很多个轮询器去监听,代码就会变得臃肿
那么针对这种异步条件的监听,我们有没有更好的方式呢?
其实分析一下我们的这个业务场景,可以发现,我们的这个业务场景有两个条件
马宝
的等级是否达到了100级
(异步执行条件condition
)马宝
的等级达到了100级
之后,给他加上一个称谓(异步执行函数executor
)
函数封装
此时我们就可以抽象出一个函数
这个函数接收两个参数
第一个参数是一个异步条件 condition
第二个参数是一个执行函数 executor
来看下我们此时的函数签名
js
const asyncCondition = (condition,executor) => {
if(condition()) executor()
}
场景是异步的,我们开启用一个轮询器去监听
把这个函数改造一下,并且返回一个 Promise
js
const asyncCondition = (condition,executor) => {
return new Promise((resolve,reject)=>{
const timer = setInterval(()=>{
if(condition()){
clearInterval(timer)
executor()
resolve()
}
},10)
})
}
彦祖们,看下此时的调用方式
js
const condition = () => {
return user.level >= 100
}
const executor = () => {
user.title = '浑元形意太极拳掌门人'
console.log('__SY__🎄 ~ executor ~ user:', user)
}
asyncCondition(condition,executor)
看下打印,没有问题,此时一个最基础的版本算是完成了
函数优化
不过对于以上函数我们还有很多需要优化的,比如
- 新增轮询的
时间间隔
参数 - 新增异步操作的
超时时间
参数,如果超过了这个时间,我们就直接 reject - 新增对函数的
参数校验
- 新增 try catch 来
捕获异常
情况
接下来让我们继续来完善,其实方向明确了,代码就非常简单了 直接上代码吧
js
const asyncCondition = (condition,executor,options = { interval: 1, timeout: 10 * 1000 }) => {
if (typeof condition !== 'function') throw new TypeError(`asyncCondition'condition must be function,but get ${typeof condition}`)
if (typeof executor !== 'function') throw new TypeError(`asyncCondition'executor must be function,but get ${typeof executor}`)
const { interval,timeout } = options
const start = performance.now() // 记录开始时间
return new Promise((resolve,reject)=>{
const timer = setInterval(()=>{
// 判断是否超时
if(performance.now() - start > timeout){
clearInterval(timer)
reject(new Error('asyncCondition timeout'))
return
}
try{
if(condition()){
clearInterval(timer)
executor()
resolve()
}
}catch(e){
clearInterval(timer)
reject(e)
}
},interval)
})
}
到此为此,函数已经比较完善了
我们可以用这个函数来解决很多异步条件的监听问题
但是笔者认为这种调用方式还是有点累赘,既然返回了一个 Promise
,就没必要再传入 executor
彦祖们,看下这行代码
js
asyncCondition(condition).then(()=>{
executor()
})
是不是亲切多了?这种形式就更加简洁,也更符合我们日常的编程习惯
到这一步应该已经非常简单了,直接上完整版代码
完整代码
js
const asyncCondition1 = (condition,options = { interval: 1, timeout: 10 * 1000 }) => {
if (typeof condition !== 'function') throw new TypeError(`asyncCondition'condition must be function,but get ${typeof condition}`)
const { interval,timeout } = options
const start = performance.now() // 记录开始时间
return new Promise((resolve,reject)=>{
const timer = setInterval(()=>{
// 判断超时
if(performance.now() - start > timeout){
clearInterval(timer)
reject(new Error('asyncCondition timeout'))
return
}
try{
if(condition()){
clearInterval(timer)
resolve()
}
}catch(e){
clearInterval(timer)
reject(e)
}
},interval)
})
}
测试下
js
asyncCondition(condition).then(executor)
再测试下异常情况
js
asyncCondition(()=>{
throw new Error('执行过程报错了')
}).then(executor).catch(e=>{
console.log('__SY__🎄 ~ e:', e)
})
同样非常完美,到此为止一个通用的异步条件监听函数
就诞生了
同事直呼好家伙,这个函数真的好用🥳
写在最后
代码虽然不多也非常简单,但是在日常工作中,需要抽象出这些公共逻辑
是非常考验彦祖们的开发能力的
个人能力有限
如有不对,欢迎指正 🌟 如有帮助,建议小心心大拇指三连🌟