前端串行请求和并发控制

112 阅读1分钟

串行请求

在一些场景下,对于多个ajax请求,需要串行执行,例如

  • 某些请求的结果,是后续请求的依赖
  • 服务器有并发请求限制
  • 某些存在顺序性的用户交互
  • 需要请求重试

实现串行请求的核心思路就是通过while循环加await

class Task {
  constructor(fn, ...restParam) {
    this.fn = fn;
    this.restParam = restParam;
  }
}

class SerialRequest {
  constructor() {
    this.queue = [];
    this.isRunning = false;
  }
  add(fn, ...restParam) {
    this.queue.push(new Task(fn, ...restParam));
  }
  async run() {
    this.isRunning = true;
    while (this.queue.length) {
      const task = this.queue.shift();
      await task.fn(...task.restParam);
    }
    this.isRunning = false;
  }
  clear() {
    this.queue = [];
  }
}

export default SerialRequest;

并发限制

在很多场景下,需要对同时发出的ajax请求进行限制

  • http1.1浏览器对于同一域名下并发数量限制一般为6-8,针对某次任务如果请求并发数过多,会阻塞其他请求
  • 调用一些第三方API经常会有QPS限制
  • 大文件分片上传/下载
class QPSLimit {
  // 单例模式
  static instance = null;
  static getInstance(...args) {
    if (!QPSLimit.instance) {
      return new QPSLimit(...args);
    }
    return QPSLimit.instance;
  }
  constructor(qps) {
    if (QPSLimit.instance) {
      return QPSLimit.instance;
    }
    this.qps = qps; // 限制的qps
    this._count = 0; // 当前qps计数
    this._taskQueue = []; // 任务队列
    QPSLimit.instance = this;
  }
  // 调用入口,如果qps未满,直接执行,否则加入任务队列
  run(caller) {
    return new Promise((resolve, reject) => {
      const task = this._createTask(caller, resolve, reject);
      if (this._count < this.qps) {
        task()
      } else {
        this._taskQueue.push(task)
      }
    })
  }
  // 创建任务,任务数量+1,执行完毕后计数-1,如果队列中有任务则继续执行
  _createTask(caller, resolve, reject) {
    return () => {
      this._count++;
      caller().then(res => {
        resolve(res);
      }).catch(err => {
        reject(err);
      }).finally(() => {
        this._count--;
        if (this._taskQueue.length) {
          this._taskQueue.shift()();
        }
      })
    }
  }
}