如何用一个定时器维护多个须要实时更新的效果

场景重现react

以前在作一个IM的模块,有一个撤回消息后两分钟以内能够从新编辑的功能,项目是用react作的,按照正常的思路,只需传入一个消息撤回的时间markTime,而后用如今时间Date.now()去判断是否已经超时两分钟。然而理想老是美好的,因为没有去触发react的从新渲染机制,即便超时了从新编辑的按钮也不会消失,等到去点击从新编辑按钮再提示已超时的话这个极大的影响用户体验。性能优化

解决方案性能

  • 思路I

能够给每条撤回的系统消息加上一个setInterval定时器轮询,只要未超时就实时更新当前时间,直至超时再把这个定时器清除掉;
可是这个方法有个弊端,就是假设我在有限时间内撤回了很是多条消息,那么这很是多条的消息就会有着对应的多个定时器在工做着,这对于性能的损耗特别的大,对于本公司的项目,团队在性能优化上仍是有追求的,因此思路I被否决掉了;优化

  • 思路II

经过静态方法维护同一个定时器订阅器,若是该订阅器中还有存在的未超过有限时间的事件,则展现其中未超过有限时间的子组件,若是已超时,则不展现其对应的自组件,返回一个null
上代码ui

/** * 经过静态方法维护同一个定时器,传入标记时间markTime和在这段有效时长alidTimeLong内,2000毫秒更新一次组件,若是超过有效时长则返回null */
import React from "react";

type UpDateFunc = (now: number) => boolean;
// 使用该组件时,须要传入两个参数,一个开始定时的时间,一个有效时长,都为number类型
export default class TimePlay extends React.Component<{
  markTime: number;
  validTimeLong: number;
}> {
  // 维护一个Set的订阅器
  static countDownFuncList = new Set<UpDateFunc>();
  // 定时器的初始状态
  static intervalHandel = -1;
  // 往订阅器中添加一个须要监听的事件
  static addFunc = (func: UpDateFunc) => {
    TimePlay.countDownFuncList.add(func);
    TimePlay.countDown();
  };
  // 订阅器中删除一个超时的事件
  static removeFunc = (func: UpDateFunc) => {
    TimePlay.countDownFuncList.delete(func);
  };
  // 订阅器中若是还存在事件,则在同一个定时器下执行
  static countDown = () => {
    if (TimePlay.intervalHandel !== -1) {
      return;
    }
    TimePlay.intervalHandel = setInterval(() => {
      if (TimePlay.countDownFuncList.size === 0) {
        clearInterval(TimePlay.intervalHandel);
        TimePlay.intervalHandel = -1;
      }
      const now = Date.now();
      for (const fn of TimePlay.countDownFuncList) {
        const isValid = fn(now);
        if (!isValid) {
          TimePlay.removeFunc(fn);
        }
      }
    }, 2000);
  };
  // 判断是否展现子组件
  state = {
    isShowChildren: this.props.markTime + this.props.validTimeLong > Date.now(),
  };
  // 用于初始时判断是否超时
  updateState = (now: number) => {
    const isValid = this.props.markTime + this.props.validTimeLong > now;
    if (isValid && !this.state.isShowChildren) {
      this.setState({ isShowChildren: true });
    }
    if (!isValid) {
      this.setState({ isShowChildren: false });
    }
    return isValid;
  };
  componentDidMount() {
    TimePlay.addFunc(this.updateState);
  }
  render() {
    if (this.state.isShowChildren) {
      return this.props.children;
    } else {
      return null;
    }
  }
}
复制代码

经过此方法,若是在实际业务当中两分钟内撤回了不少条消息,依旧只须要静态方法中的一个定时器去轮询,而无需同时开启多个定时器。this

固然,若是对render中的return结果进行改造,还能够应用于抢购或其余场景下同个页面有多个倒计时的场景。spa

若是你有更好的解决方案,欢迎讨论~code

相关文章
相关标签/搜索