Coding:调用接口并发请求控制(蚂蚁面试题)

742 阅读1分钟

优化 requestUserProfile 并发请求

requestUserProfile 是个通用用户信息接口,通过传入uid,拿用户昵称 在一个群聊里有10多个用户,点击群聊信息,展示各个人的昵称 10个并发请求,会阻塞接口 10个依次请求,耗时久,显示昵称太慢 需要优化请求,在并发和耗时之间掌握一个平衡

import { isEqual } from 'lodash-es';

// 核心用户请求
let _requestTime = 0;
const requestProfile = (uid: string) => {
  // 这个方法的实现不能修改
  return Promise.resolve().then(() => {
    return new Promise<void>((resolve) => {
      setTimeout(() => {
        // 模拟 ajax 异步,1s 返回
        resolve();
      }, 1000);
    }).then(() => {
      _requestTime++;
      return {
        uid,
        nick: `nick-${uid}`,
        age: '18',
      };
    });
  });
};

/**
 * @param uid uid
 * @param max 最多并发请求数量
 */
const requestUserProfile =  (uid = '1', max = 2) => {
  // 这里调用requestProfile 进行优化
};

/**
 * 以下为测试用例,无需修改
 */
export default async () => {
  try {
    const star = Date.now();
    await Promise.all([
      requestUserProfile('1'),
      requestUserProfile('2'),
      requestUserProfile('3'),
      requestUserProfile('1'),
    ]).then((result) => {
      if (Date.now() - star < 2000 || Date.now() - star >= 3000) {
        throw new Error('Wrong answer');
      }
      if (
        !isEqual(result, [
          {
            uid: '1',
            nick: 'nick-1',
            age: '18',
          },
          {
            uid: '2',
            nick: 'nick-2',
            age: '18',
          },
          {
            uid: '3',
            nick: 'nick-3',
            age: '18',
          },
          {
            uid: '1',
            nick: 'nick-1',
            age: '18',
          },
        ])
      ) {
        throw new Error('Wrong answer');
      }
    });

    return _requestTime === 3;
  } catch (err) {
    console.warn('测试运行失败');
    console.error(err);
    return false;
  }
};






参考答案1

let count = 0;
const memo = {};

/**
 * @param uid uid
 * @param max 最多并发请求数量
 */
const requestUserProfile = async (uid = '1', max = 2) => {
  // 这里调用requestProfile 进行优化
  let timer;
  const loopRequestProfile = (resolve: any) => {
    timer && cancelAnimationFrame(timer);
    if (memo[uid]) {
      return resolve(memo[uid])
    }
    if (max > count) {
      count += 1;
      requestProfile(uid).then((data) => {
        count -= 1;
        memo[uid] = data;
        resolve(data);
      });
    } else {
       // 循坏调用 loopRequestProfile 直到 count < max
      timer = requestAnimationFrame(() => {
        loopRequestProfile(resolve);
      });
    }
  };

  return new Promise(loopRequestProfile);
}

✅ 参考答案2


let count = 0;
const taskQueue = [];

/**
 * @param uid uid
 * @param max 最多并发请求数量
 */
const requestUserProfile =  (uid = '1', max = 2) => {
  const pullTask = () => {
    if (taskQueue.length === 0) {
      return;
    }
    if (count >= max) {
      return;
    }
    count++;
    const { resolve, uid } = taskQueue.shift();
    resolve(runTask(uid));
  };
  
  const runTask = (id) => {
    const promise = requestProfile(id);
    promise.then(() => {
      count--;
      pullTask();
    });
    return promise;
  }; 
  
  return new Promise((resolve) => {
    taskQueue.push({ resolve, uid });
    if (count < max) {
      count++;
      const { resolve, uid  } = taskQueue.shift();
      resolve(runTask(uid));
    }
  })
};