单例模式实践-合并发送请求

91 阅读3分钟

背景:

实际工作中,接口写在不同组件中,但是请求却是相同的(有人可能会说为什么接口不提出来,我只能说特殊场景,只能这么做;没必要纠结这个,重点是单例模式使用的这套思想)

下面是单例模式代码:

class Singleton {
  static instance;

  // 创建实例的时候走这儿,在这儿也要判断是否是否创建了实例,要保证是单例模式
  constructor() {
    // 承接请求后拿到的结果
    this.result;
    // 私有属性承接请求
    this._request;
    // 这是私有属性,不允许外部使用
    this._hasFinish = "init"; //  初始值:init,请求进行中:doing,请求已结束:done

    // 没实例就创建
    if (!Singleton.instance) {
      Singleton.instance = this;
    }
    // 有实例的化,直接返回
    return Singleton.instance;
  }

  // 如果用户使用通过 Singleton.getInstance方式调用,也要保证是单例的
  static getInstance() {
    // 没实例就创建
    if (!this.instance) {
      this.instance = new Singleton();
    }
    // 有实例的化,直接返回
    return this.instance;
  }

  onRequest = async (name, age) => {
    // 没请求就赋值
    if (!this._request) {
      this._request = onHandle(name, age);
    }
    // 未开始和进行中,都要走进去请求;只有已结束,说明拿到值了,这时候就不用进去发起请求了
    if (this._hasFinish !== "done") {
      // 不管实例几次,都是同一个请求你
      const res = await this._request;
      this.result = res;

      // 1已结束,就改状态
      this._hasFinish = "done";
    }

    return this.result;
  };

  // 重置;就会恢复到初始状态,只有在这个状态才能再次发送请求;一旦发送请求后,不重置的话,是没法发请求的;所以必须重置;项目关闭例外;
  onReset = () => {
    this.result = undefined;
    this._request = undefined;
    this._hasFinish = "init";
  };
}

模拟一个接口请求:

const onHandle = (name, age) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name, age });
      console.log("测试走了几次");
    }, 1000);
  });
};

下面是测试用例:


let instance1;
let instance2;
let instance3;

const test = async () => {
  // 通过new的方式调用
  // instance1 = new Singleton();
  // 通过静态方法调用
  instance1 = Singleton.getInstance();
  const res = await instance1.onRequest("老王", 111);
  console.log("1--:", res);
};
const test2 = async () => {
  // 通过new的方式调用
  // instance2 = new Singleton();
  // 通过静态方法调用
  instance2 = Singleton.getInstance();
  const res = await instance2.onRequest("大刚", 222);
  console.log("2--:", res);
};
const test3 = async () => {
  // 通过new的方式调用
  // instance3 = new Singleton();
  // 通过静态方法调用
  instance3 = Singleton.getInstance();
  const res = await instance3.onRequest("小明", 333);
  console.log("3--:", res);
};

// 顺序连着请求,模拟的是页面初始化进来不同页面同时发出的请求
test();
test2();
// 测试否是 是单例
console.log(instance2 === instance1);
// 测试是否 是同一个请求
console.log(instance1.req === instance2.req);

// 延迟了5秒,模拟的是页面初始化完毕,用户点击操作发出的请求
setTimeout(() => {
  test3();
}, 5000);

运行结果:

image.png

分析:

测试用例中,写了两种方式调用,一种是通过new实例的方式,一种是通过静态方法调用的方式;

模拟了两种场景,起码是满足我工作中的要求;一种场景是不同页面同时调用该接口,另一种是用户操作调用接口;

最后的结果是,不管调用几次,只发送了一次请求;实现了合并请求的目标;

其实,第一种场景是连续请求,其实是只发送了一个公共接口;第二种场景,即用户点击,其实是第一种场景已经拿到结果了,第二种它返回的是上一次的结果。

感悟:研究思想比重复写业务更有乐趣!