我如何实现一个轮询函数的封装?

2,716 阅读3分钟

最近更新

7.28 新增了超时时间参数

前言

阅读完本文,大概需要 5 分钟 时间

你将会学到

1. 基础的函数封装思想

2. 函数冗余代码的抽象

项目背景

笔者最近在项目开发阶段,经常遇到这样的场景

当满足 xx 条件的时候,才去执行 xx 函数,其实最简单的方式就是利用回调函数

为了各位看官能够感同身受,下面由 boduo 老师为大家上课

场景复现

// 我们初始化一个叫做 `boduo` 的演员,此时 ta 还是八岁 
const actor = {
  name: 'bodo',
  age: 8
}

// 我们再自定义模拟一个时间增长函数
// 假设每 200ms 增长一岁
const ageGrow = () => {
  setInterval(() => {
    actor.age += 1
    // 假设 boduo 长到 18 岁的时候获取到了新技能  movie
    if (actor.age >= 18) {
      actor.movie = function() {
        const { name, age } = actor
        console.log(`我叫 ${name},今年${age}岁, 我现在会拍电影啦`)
      }
    }
  }, 200)
}
ageGrow()

场景很简单, 现在有一位经纪人看中了这位小演员,但是此时 ta 还不会拍电影,所以经纪人需要每过200ms(一年)询问一下这位小演员是否能够拍摄电影,不然就可能导致报错翻车

模拟成代码

const makeingMovie = () => {
  //actor.movie() //直接调用报错
  if (actor.movie) {
    actor.movie()
  } else {
    setTimeout(makeingMovie, 200)
  }
}

以上就是我们的常规思路,利用不断轮询判断某个条件是否成立

完整的常规代码

const actor = {
  name: 'bodo',
  age: 8
}

const ageGrow = () => {
  setInterval(() => {
    actor.age += 1
    if (actor.age >= 18) {
      actor.movie = function() {
        const { name, age } = actor
        console.log(`我叫 ${name},今年${age}岁, 我现在会拍电影啦`)
      }
    }
  }, 200)
}
ageGrow()
const makeingMovie = () => {
  if (actor.movie) {
    actor.movie()
  } else {
    setTimeout(makeingMovie, 200)
  }
}
makeingMovie()

函数封装

简单记录下自己的思路吧 其实无非就是三个条件

  1. 对应的执行函数
  2. 对应的条件函数
  3. 轮询的时间延迟

有了这三个条件,我们能够简单得到以下函数框架

const fn = (executor,condition,delay)=>{}

一开始笔者设计的是没有 return 值的,也就是直接把业务代码在 fn 中执行了,这样的做法就导致耦合度非常高,而且不好传参

借鉴 debounce 防抖的思想,最终决定 return 一个函数出去

const fn = (executor,condition,delay)=>{
    if(condition()){
        return function(...args){
            return executor(...args)
        }
    }
}

但是condition的代码需要不断轮询,直接 return函数并没有什么软用

所以此时又借助了promise

学习 promise 控制并发技能,强烈推荐各位看官阅读下我这篇文章

我如何控制大屏看板 1000个 Echarts 渲染并发?

封装代码

const getFuncOnLoopCondition = (executor, condition, delay = 10,timeout=10 * 1000) => {
  if (typeof executor !== 'function') return console.warn(`getFuncOnLoopCondition'executor must be function,but get ${typeof executor}`)
  if (typeof condition !== 'function') return console.warn(`getFuncOnLoopCondition'condition must be function,but get ${typeof condition}`)
  const start = performance.now()
  let timer = null
  return new Promise((resolve, reject) => {
    const loop = function(executor, condition, delay) {
      if (performance.now() - start > timeout) {
        // 超时了
        if (timer) clearTimeout(timer)
        return reject(`调用超时了`)
      }
      const finished = condition.apply(this) // 判断条件是否成立
      if (finished) {
        if (timer) clearTimeout(timer)
        const res = function(...args) { // 这里的 args 就是传递给 executor 的
          return executor.apply(this, args)
        }
        resolve(res) // 此时已经满足条件 直接返回函数,函数主体为执行函数
      } else {
        timer = setTimeout(() => {
          loop(executor, condition, delay) // 不断轮询
        }, delay)
      }
    }
    loop.call(this, executor, condition, delay)
  })
}

下面让我们用封装代码简单测试一下刚才的场景

const getFuncOnLoopCondition = (executor, condition, delay = 10) => {
  if (typeof executor !== 'function') return console.warn(`getFuncOnLoopCondition'executor must be function,but get ${typeof executor}`)
  if (typeof condition !== 'function') return console.warn(`getFuncOnLoopCondition'condition must be function,but get ${typeof condition}`)
  const start = performance.now()
  let timer = null
  return new Promise((resolve, reject) => {
    const loop = function(executor, condition, delay) {
      if (performance.now() - start > timeout) {
        // 超时了
        if (timer) clearTimeout(timer)
        return reject(`调用超时了`)
      }
      const finished = condition.apply(this)
      if (finished) {
        if (timer) clearTimeout(timer)
        const res = function(...args) {
          return executor.apply(this, args)
        }
        resolve(res)
      } else {
        timer = setTimeout(() => {
          loop(executor, condition, delay)
        }, delay)
      }
    }
    loop.call(this, executor, condition, delay)
  })
}

const actor = {
  name: 'bodo',
  age: 8
}
const ageGrow = () => {
  setInterval(() => {
    actor.age += 1
    // 假设 boduo 长到 18 岁的时候获取到了新技能  movie
    if (actor.age >= 18) {
      actor.movie = function() {
        const { name, age } = actor
        console.log(`我叫 ${name},今年${age}岁, 我现在会拍电影啦`)
      }
    }
  }, 200)
}
ageGrow()

const executor = (unitPrice) => {
  console.log(`售价 ${unitPrice}`)
  actor.movie()
}
const condition = () => actor.age >= 18

const makeingMovie = async() => {
  // 返回一个 promise 我们需要 借助 async await,或者.then
  const fn = await getFuncOnLoopCondition(executor, condition)
  const unitPrice = '20元'
  fn(unitPrice)
}
makeingMovie()

最后

如果这篇文章对你有帮助,建议小心心大拇指三连🌟

个人能力有限,若有不正确地方,请指出,望读者们不喜勿喷🌟