如何封装一个请求,让其多次调用的时候,实际只发起一个请求的时候,返回同一份结果【热度: 636】

关键词:defer函数、请求结果缓存在JS内存

最优解: 使用deferred思想来实现请求的等待队列,可以借助Promise和async/await语法

下面是使用deferred思想来实现的代码示例:

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }
}

// 创建一个全局的锁标识
let lock = false;

// 创建一个缓存对象
const cache = {};

// 创建一个等待队列数组
const waitingRequests = [];

// 封装请求函数
async function request(url, params) {
  const cacheKey = `${url}-${JSON.stringify(params)}`;

  // 判断锁的状态
  if (lock) {
    const deferred = new Deferred();
    // 如果锁已经被占用,将请求添加到等待队列中
    waitingRequests.push({
      deferred,
      cacheKey
    });
    await deferred.promise;
    return cache[cacheKey];
  }

  // 设置锁的状态为true,表示当前请求正在执行
  lock = true;

  try {
    // 发起实际的请求
    const response = await fetch(url, params);
    const data = await response.json();
    // 将结果存入缓存对象
    cache[cacheKey] = data;
    return data;
  } finally {
    // 释放锁,将锁的状态设置为false
    lock = false;

    // 处理等待队列中的请求
    if (waitingRequests.length > 0) {
      const request = waitingRequests.shift();
      request.deferred.resolve(cache[request.cacheKey]);
    }
  }
}

// 调用请求函数
request('https://api.example.com/data', { method: 'GET' })
  .then(data => {
    // 处理请求结果
    console.log(data);
  });

// 同时发起另一个请求
request('https://api.example.com/data', { method: 'GET' })
  .then(data => {
    // 直接从缓存中获取结果,而不发起实际的请求
    console.log(data);
  });

在上述代码中,Deferred类用于创建一个延迟对象,其中promise属性是一个Promise对象,resolvereject方法分别用于解决和拒绝该延迟对象的promise。通过await关键字等待延迟对象的promise完成,当锁被占用时,将请求添加到等待队列中,并使用await等待对应的延迟对象的promise完成后再返回结果。当请求完成后,解锁并处理等待队列中的请求。