基于 Promise 实现异步任务队列 2

实现一个请求队列,要求同时请求数不能超过最大限制,调用方式的伪代码如下所示:

  1. // 第二个参数可以是fetch、axios等
  2. // const request = createRequest(6, fetch);
  3. // for (let i = 1; i <= 10; i++) {
  4. // request({ url: `xxx` }).then(res => {
  5. // // do somethings
  6. // });
  7. // }
JavaScript

首先对以上伪代码进行分析:

  • createRequest 的返回值是一个函数
  • request 是一个 Promise 实例
  • request 可以重复多次调用

代码实现

  1. /**
  2. * 请求数量控制
  3. * @param {number} maxRequestCount 同时最大请求数量
  4. * @param {(opts:any)=>Promise} asyncFunc 异步请求函数,可以是axios,fetch等
  5. * @returns
  6. */
  7. function createRequest(maxRequestCount, asyncFunc) {
  8. // 正在请求中的request数量
  9. let requestingCount = 0;
  10. // 请求的队列
  11. let queue = [];
  12. function run() {
  13. // 如果正在请求的数量小于允许的最大数量,并且有需要执行的新的请求
  14. while (requestingCount < maxRequestCount && queue.length > 0) {
  15. // 从队列中取出待执行的任务
  16. const [options, resolve, reject] = queue.shift();
  17. requestingCount++;
  18. // 执行函数
  19. asyncFunc(options)
  20. .then(resolve)
  21. .catch(reject)
  22. .finally(() => {
  23. // 执行完成之后,将正在进行的任务数量减1
  24. requestingCount--;
  25. // 递归调用,判断是否有新的任务需要执行
  26. run();
  27. });
  28. }
  29. }
  30. return function (options) {
  31. // 返回一个promise对象
  32. return new Promise((resolve, reject) => {
  33. // 将收集到的参数信息及回调函数等放入请求队列
  34. queue.push([options, resolve, reject]);
  35. // 执行请求
  36. run();
  37. });
  38. };
  39. }
JavaScript

测试

  1. // 模拟一个异步请求
  2. function AsyncRequest(options) {
  3. const { url, method } = options;
  4. // 实际开发的时候直接返回一个axios、fetch等
  5. // return axios(url, options);
  6. // 模拟axios、fetch等返回一个Promise对象
  7. return new Promise(resolve => {
  8. // 随机延迟2-5s
  9. const timeout = Math.floor(Math.random() * 3) + 2;
  10. console.log(`${url} begin request.`);
  11. // 模拟接口情况
  12. setTimeout(() => {
  13. resolve(`${url} ${method} finished,Take ${timeout} seconds.`);
  14. }, timeout * 1e3);
  15. });
  16. }
  17. // 记录队列开始执行时间
  18. console.time('all time');
  19. console.time('main');
  20. // 设置最大请求数量
  21. const request = createRequest(6, AsyncRequest);
  22. // 实际开发的时候直接传入axios、fetch等第三方js库函数即可
  23. // const request = createRequest(6, axios);
  24. // 这里模拟进入页面之后,需要默认加载的一些数据
  25. for (let i = 1; i <= 10; i++) {
  26. request({ url: `url${i}`, method: 'get', data: `urlData${i}` }).then(res =>
  27. console.log(res)
  28. );
  29. }
  30. // 模拟用户行为,比如10s后用户点击了查询操作,触发了接口请求
  31. setTimeout(() => {
  32. request({ url: 'url_timeout', method: 'post', data: 'urlData_timeout' }).then(
  33. res => {
  34. console.log(res);
  35. console.timeEnd('all time');
  36. }
  37. );
  38. }, 10 * 1e3);
  39. // console.timeEnd('main');
  40. // url1 begin request.
  41. // url2 begin request.
  42. // url3 begin request.
  43. // url4 begin request.
  44. // url5 begin request.
  45. // url6 begin request.
  46. // main: 2.934ms
  47. // url4 get finished,Take 2 seconds.
  48. // url7 begin request.
  49. // url3 get finished,Take 3 seconds.
  50. // url8 begin request.
  51. // url1 get finished,Take 4 seconds.
  52. // url9 begin request.
  53. // url2 get finished,Take 4 seconds.
  54. // url10 begin request.
  55. // url5 get finished,Take 4 seconds.
  56. // url6 get finished,Take 4 seconds.
  57. // url8 get finished,Take 2 seconds.
  58. // url7 get finished,Take 4 seconds.
  59. // url10 get finished,Take 2 seconds.
  60. // url9 get finished,Take 4 seconds.
  61. // url_timeout begin request.
  62. // url_timeout post finished,Take 4 seconds.
  63. // all time: 14.008s
JavaScript
编程笔记 & 随笔杂谈