JS实现请求防抖

实现一个函数:

  • 对于同一个请求,如果上一次接口还没有返回,不进行下一次调用
  • 接口返回后,调用该请求的所有回调函数

代码实现

  1. /**
  2. * 请求控制,对于url请求未完成之前,不发起重复的请求
  3. * @param {(options)=>Promise} asyncRequest fetch、axios等
  4. * @param {number} maxRetryCount 最大重试次数
  5. * @returns
  6. */
  7. function reqDebounce(asyncRequest, maxRetryCount) {
  8. // 收集每个请求的回调函数
  9. const map = new Map();
  10. // 记录每个请求重试的次数
  11. const retryReqMap = new Map();
  12. // 定义一个私有属性,用来判断是否是重试
  13. const privateRetry = Symbol('retry');
  14. // 返回一个Promise对象
  15. return function request(options, retry) {
  16. const { url, method, data } = options;
  17. // 这里生成唯一key的规则可以自己改写
  18. const onlyKey = `${method}_${url}_${data}`;
  19. return new Promise((resolve, reject) => {
  20. if (retry !== privateRetry) {
  21. if (map.has(onlyKey)) {
  22. map.get(onlyKey).push([resolve, reject]);
  23. return;
  24. } else {
  25. map.set(onlyKey, [[resolve, reject]]);
  26. }
  27. }
  28. asyncRequest(options)
  29. .then(res => {
  30. // 如果请求成功了,将重试次数清空
  31. retryReqMap.set(onlyKey, 0);
  32. // 调用url收集的所有回调函数
  33. map.get(onlyKey).forEach(([resolveFunc]) => resolveFunc(res));
  34. // 清空url收集的回调函数
  35. map.set(onlyKey, []);
  36. })
  37. .catch(ex => {
  38. // 已经重试的次数
  39. let count = retryReqMap.get(onlyKey) || 0;
  40. if (count < maxRetryCount) {
  41. // 重试次数+1
  42. retryReqMap.set(onlyKey, ++count);
  43. console.log(`第${count}次尝试重新请求${url}`);
  44. // 尝试重试
  45. request(options, privateRetry);
  46. } else {
  47. // 达到了最大重试次数,执行reject回调函数
  48. map.get(onlyKey).forEach(([_, rejectFunc]) => rejectFunc(ex));
  49. retryReqMap.set(onlyKey, 0);
  50. }
  51. });
  52. });
  53. };
  54. }
JavaScript

测试

  1. // 模拟一个异步请求
  2. function AsyncRequest(options) {
  3. const { url } = options;
  4. console.log(`${url} is called.`);
  5. // 实际开发的时候直接返回一个axios、fetch等
  6. // return axios(url, options);
  7. // 模拟axios、fetch等返回一个Promise对象
  8. return new Promise((resolve, reject) => {
  9. // 随机延迟2-5s
  10. const timeout = Math.floor(Math.random() * 1) + 1;
  11. // 模拟接口情况
  12. setTimeout(() => {
  13. Math.random() > 0.5 ? resolve(`${url} success`) : reject(`${url} error`);
  14. }, timeout * 1e3);
  15. });
  16. }
  17. // const request = reqDebounce(fetch, 3);
  18. const request = reqDebounce(AsyncRequest, 3);
  19. const opts = { method: 'get', data: 'testData' };
  20. const opts1 = { ...opts, url: 'url1' };
  21. const opts2 = { ...opts, url: 'url2' };
  22. request(opts1).then(console.log).catch(console.log);
  23. request(opts1).then(console.log).catch(console.log);
  24. request(opts2).then(console.log).catch(console.log);
  25. request(opts2).then(console.log).catch(console.log);
  26. // url1 is called.
  27. // url2 is called.
  28. // url1 success
  29. // url1 success
  30. // 第1次尝试重新请求url2
  31. // url2 is called.
  32. // 第2次尝试重新请求url2
  33. // url2 is called.
  34. // url2 success
  35. // url2 success
JavaScript

控制台打印结果不唯一,因为函数的成功与否是随机的

编程笔记 & 随笔杂谈