前言
彦祖们,在我们日常开发中经常会遇到这样一个场景
轮询某个异步条件是否满足,如果满足再去执行某个函数
业务场景复现
笔者现在模拟一个业务场景
有一个叫马宝的大师,他的技能有 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)
})
        
同样非常完美,到此为止一个通用的异步条件监听函数就诞生了
同事直呼好家伙,这个函数真的好用🥳
写在最后
代码虽然不多也非常简单,但是在日常工作中,需要抽象出这些公共逻辑
是非常考验彦祖们的开发能力的
个人能力有限
如有不对,欢迎指正 🌟 如有帮助,建议小心心大拇指三连🌟