重复请求取消(不发请求)

news2024/11/30 13:35:16

重复请求取消(不发请求)

axios 有自带的取消请求方式, 但是 在请求已发出时,取消只是状态取消, 其实请求已经发出,消耗了服务器资源(方式1)。本文探究的是 在调用请求A时, 在A未响应之前,所有的A重复请求都应该不发出,待A响应之后,将响应结果给到之前的所有A请求!(方式2,3)

基础代码

// request.js
export const request = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

// api/user.js  获取数据函数, request 就是axios实例
export function getCrypto1(params) {
  return request({
    url: "/echo",
    method: "post",
    params,
    encryption: false,
    hideMsg: true,
    notSign: true,
    notToken: true
  });
}

// App.vue created

function sleep(time) {
   return new Promise((resolve) => setTimeout(resolve, time));
}

for (let i = 0; i < 5; i++) {
  console.log("for: ", i);
  // await sleep(500);
  getCrypto1({ a: 11111 }).then((res) => {
    console.log("res--P: ", res);
  }).catch((err) => {
    console.log("res--E: ", err);
  });
}

#1 axios自带的取消请求(AbortController)

完整代码(request.js):

// 用于存储请求标识和对应的 AbortController
const pendingRequests = new Map();

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    cancelRequestHandler(config);
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    cancelRequestHandler(response.config, "repsonse");
  },
  (error) => {
    if (error.config) {
      cancelRequestHandler(error.config, "repsonse");
    }
    if (error.name === "CanceledError") {
      return;
    }
    return Promise.reject(error.response);
  }
);

// 生成唯一请求标识的方法
function generateRequestKey(config) {
  const { method, url, params, data } = config;
  return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;
}

// axios AbortController取消请求
function cancelRequestHandler(config, requestType = "request") {
  // console.log("--cancel:  ", config, requestType);
  // 创建请求标识
  const requestKey = generateRequestKey(config);

  if (requestType === "request") {
    // 如果已存在相同请求,取消之前的请求
    if (pendingRequests.has(requestKey)) {
      const controller = pendingRequests.get(requestKey);
      controller.abort();
      pendingRequests.delete(requestKey);
    }

    // 为当前请求创建新的 AbortController
    const controller = new AbortController();
    config.signal = controller.signal;

    // 存储当前请求的控制器
    pendingRequests.set(requestKey, controller);
  } else {
    pendingRequests.delete(requestKey);
  }
}

请添加图片描述

#2 不发请求(axios)

大概思路就是 将重复请求在请求拦截器里用 return Promise.reject 直接进入响应拦截器错误里,在将结果返回即可

完整代码(request.js):

export const request = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

// 用于存储请求标识
const pendingRequests = new Map();

// 请求拦截器配置
request.interceptors.request.use(
  (config) => {
    const { shouldIntercept, promise } = handleDuplicateRequest(config);
    if (shouldIntercept) {
      return promise; // 如果是重复请求,返回挂起的 Promise
    }
    // 其它逻辑...
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
request.interceptors.response.use(
  (response) => {
    resolvePendingQueue(response);
    // 其它逻辑...
    return Promise.resolve(decrypts);
  },
 (error) => {
    console.log("resp error: ", error);
    if (error.cachedResponse) {
      if (error.cachedResponse.status === 200) {
        return Promise.resolve(error.cachedResponse?.data);
      } else {
        return Promise.reject(error.cachedResponse?.data);
      }
    }
    if (error.config) {
      rejectPendingQueue(error.response);
    }
    // 其它逻辑...
    return Promise.reject(error.response);
  }
);

// 生成唯一请求标识的方法
function generateRequestKey(config) {
  const { method, url, params, data } = config;
  return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`;
}

// 重复请求缓存
function handleDuplicateRequest(config) {
  const requestKey = generateRequestKey(config);
  if (pendingRequests.has(requestKey)) {
    return {
      shouldIntercept: true,
      promise: new Promise((resolve, reject) => {
        pendingRequests.get(requestKey).push({ resolve, reject });
      })
    };
  }
  // 如果是首次请求,初始化队列
  pendingRequests.set(requestKey, []);
  return { shouldIntercept: false, promise: null };
}

// 处理成功响应队列
function resolvePendingQueue(response) {
  const requestKey = generateRequestKey(response.config);
  const pendingQueue = pendingRequests.get(requestKey) || [];
  pendingQueue.forEach(({ reject }) => reject({ cachedResponse: response }));
  pendingRequests.delete(requestKey);
}

// 处理失败响应队列
function rejectPendingQueue(error) {
  const requestKey = generateRequestKey(error.config);
  const pendingQueue = pendingRequests.get(requestKey) || [];
  pendingQueue.forEach(({ reject }) => reject({ cachedResponse: error }));
  pendingRequests.delete(requestKey);
}

示例((成功):

请添加图片描述

示例(失败):
请添加图片描述

以上只发了一次请求

#3 不发请求(推荐,适合所有请求库)

基本思路: 对于第一个请求, 将promise存起来, 在该请求响应之前, 所有的相同请求都公用一个promise, 当第一个请求响应时,所有的promise都释放了,所有阻塞的相同请求都能拿到相同的结果!

完整代码(request.js)

export const request = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

// 创建缓存请求
export function createCacheedRequest(config) {
  // 根据请求的 URL 和参数生成唯一的请求 key
  const requestKey = generateRequestKey(config);
  // 判断请求是否已经在进行中
  if (!pendingRequests.has(requestKey)) {
    // 如果没有该请求,发起请求并缓存该请求的 Promise
    const requestPromise = service(config).finally(() => {
      // 请求完成后,从 pendingRequests 中删除该请求
      pendingRequests.delete(requestKey);
    })

    // 缓存该请求的 Promise
    pendingRequests.set(requestKey, requestPromise);
  }

  // 返回正在进行的请求 Promise
  return pendingRequests.get(requestKey);
}

// 不用再拦截器添加代码

// 调用的方式需要改一下
// 获取数据
export function getCrypto1(params) {
  return createCacheedRequest({
    url: "/echo",
    method: "post",
    params,
    encryption: false,
    hideMsg: true,
    notSign: true,
    notToken: true
  });
}

请添加图片描述

当打开睡眠时间时

for (let i = 0; i < 5; i++) {
  await sleep(500); // 每过500ms发出一个请求
  console.log("for: ", i);
  getCrypto1({ a: 1111 }).then((res) => {
    console.log("res--P: ", res);
  }).catch((err) => {
    console.log("res--E: ", err);
  });
  // this.getCryptoData();
}

// node服务
// 接口耗时1500ms
app.post('/echo', (req, res) => {
  console.log('received : ', req.body)
  const data = req.body;
  setTimeout(() => {
    res.json({ code: 0, msg: 'success', data: 22222 });
    // res.status(500).json({ code: -1, msg: 'error', data: null });
  }, 1500)
});

这样就会第1个请求结束后,和第2,3,4 一起返回, 第五个请求重新发,不会被取消,单独返回。总共发出了两次请求

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2250452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌浏览器Chrome打开百度很慢,其他网页正常的解决办法,试了很多,找到了适合的

最近不知怎么的&#xff0c;Chrome突然间打开百度很慢&#xff0c;甚至打不开。不光我一个人遇到这问题&#xff0c;我同事也遇到这个问题。开发中难免遇到问题&#xff0c;需要百度&#xff0c;现在是百度不了。 作为一名开发人员&#xff0c;习惯了使用Chrome进行开发&#…

【C++】深入探讨基础输入输出及类型转换问题

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目一&#xff1a;整数输入输出代码实现代码分析 &#x1f4af;题目二&#xff1a;ASCII 码转换为字符代码实现代码分析 &#x1f4af;cin 与 char 搭配行为的深入剖析问题…

GB28181系列三:SIP消息格式

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、SIP消息Header字段 二、SIP URI(URL) 三、SIP路由机制 1、路由机制介绍 2、严格路由&#xff08;Strict Routing&#xff09;与松散路由&#xff08;Louse Routing&#xff09; 3、总结 四、SIP消…

mvn-mac操作小记

1.安装brew 如果报错&#xff0c;Warning: /opt/homebrew/bin is not in your PATH. vim ~/.zshrc&#xff0c;最后一行追加 export PATH“/opt/homebrew/bin:$PATH” source ~/.zshrc 2.安装brew install maven mvn -version查看路径 Maven home: /opt/homebrew/Cellar/mav…

算法与数据结构练习——异或

知识点讲解&#xff1a; 一、异或操作定义&#xff1a; 异或是指相同为0&#xff0c;不同为1&#xff0c;也可理解为无进位相加&#xff01;&#xff01; 很重要&#xff01;&#xff01; 二、关于异或运算的几个性质&#xff1a; 1.0^NN &#xff08;0和任何数异或都…

STM32G4系列MCU的Direct memory access controller (DMA)功能介绍之二

目录 概述 1 DMA通道 1.1 可编程数据大小 1.2 指针增量 2 通道配置 2.1 配置步骤 2.2 通道状态和禁用通道 3 模式应用 3.1 循环模式&#xff08;内存到外设/外设到内存的传输&#xff09; 3.2 内存到内存模式 3.3 Peripheral-to-peripheral模式 3.4 编程转移方向&a…

【一文读懂】大语言模型

学习参考 项目教程&#xff1a;中文教程 代码仓库&#xff1a;代码地址 仓库代码目录说明&#xff1a; requirements.txt&#xff1a;官方环境下的安装依赖 notebook&#xff1a;Notebook 源代码文件 docs&#xff1a;Markdown 文档文件 figures&#xff1a;图片 data_base&…

注册表修改键盘位置

1.winr 输入 regedit 2.HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout 3.右键Keyboard Layout->新建->二进制值->取名Scancode Map 4.右键Scancode Map&#xff0c;修改如下 //第一列 自动生成序号&#xff0c;不用管 第一行 输入8个00 第二…

服务器密码错误被锁定怎么解决?

当服务器密码错误多次导致账号被锁定时&#xff0c;解决方法需要根据服务器的操作系统&#xff08;如 Linux 或 Windows &#xff09;和具体服务器环境来处理。以下是常见的解决办法&#xff1a; 一、Linux 服务器被锁定的解决方法 1. 使用其他用户账号登录 如果有其他未被…

shell脚本编写练习2

1.准备阶段 在根目录下创建一个目录来存储shell脚本 mkdir /s3 2.题目 1. 使用case实现成绩优良差的判断 #!/bin/bash# 假设成绩存储在变量GRADE中 read -p "请输入成绩&#xff08;0-100&#xff09;&#xff1a;" GRADE# 使用case语句进行判断 case $GRADE in[…

清远榉之乡托养机构探讨:自闭症的本质辨析

当人们谈及自闭症时&#xff0c;常常会产生一个疑问&#xff1a;自闭症是精神类疾病吗&#xff1f;今天&#xff0c;清远榉之乡托养机构就来为大家解开这个疑惑。 榉之乡大龄自闭症托养机构在江苏、广东、江西等地都有分校&#xff0c;一直致力于为大龄自闭症患者提供专业的支持…

基于PoE交换机的智慧停车场监控组网应用

伴随城市发展快速&#xff0c;汽车保有量也不断增长&#xff0c;导致停车管理问题也愈发凸显。针对包括路侧停车位、地面停车场、地下停车场等场景的停车管理需求&#xff0c;通常会部署监控设备进行车位监测、现场安全监测等&#xff0c;助力构建智能化停车管理。因此如何为分…

.net XSSFWorkbook 读取/写入 指定单元格的内容

方法如下&#xff1a; using NPOI.SS.Formula.Functions;using NPOI.SS.UserModel;using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;/// <summary>/// 读取Excel指定单元格内容/// </summa…

10个Word自动化办公脚本

在日常工作和学习中&#xff0c;我们常常需要处理Word文档&#xff08;.docx&#xff09;。 Python提供了强大的库&#xff0c;如python-docx&#xff0c;使我们能够轻松地进行文档创建、编辑和格式化等操作。本文将分享10个使用Python编写的Word自动化脚本&#xff0c;帮助新…

红日靶场-5

环境搭建 这个靶场相对于前几个靶场来说较为简单&#xff0c;只有两台靶机&#xff0c;其中一台主机是win7&#xff0c;作为我们的DMZ区域的入口机&#xff0c;另外一台是windows2008&#xff0c;作为我们的域控主机&#xff0c;所以我们只需要给我们的win7配置两张网卡&#…

[Java]微服务之分布式事务

介绍 下单业务&#xff0c;前端请求首先进入订单服务&#xff0c;创建订单并写入数据库。然后订单服务调用购物车服务和库存服务: 购物车服务负责清理购物车信息库存服务负责扣减商品库存 问题分析: 下单过程中, 订单服务创建订单, 插入自己的数据库, 执行成功购物车服务, 清…

存储结构及关系(一)

学习目标 描述数据库的逻辑结构列出段类型及其用途列出控制块空间使用的关键字获取存储结构信息 段的类型 段是数据库中占用空间的对象。它们使用数据库数据文件中的空间。介绍不同类型的段。 表 表是在数据库中存储数据的最常用方法。表段用于存储既没有集群也没有分区的表…

cesium 3dtile ClippingPlanes 多边形挖洞ClippingPlaneCollection

原理就是3dtiles里面的属性clippingPlanes 采用ClippingPlaneCollection&#xff0c;构成多边形来挖洞。 其次就是xyz法向量挖洞 clippingPlanes: new this.ffCesium.Cesium.ClippingPlaneCollection({unionClippingRegions: true, // true 表示多个切割面能合并为一个有效的…

AMD的AI芯片Instinct系列介绍

AMD最强AI芯片发布&#xff01; 在旧金山举行的Advancing AI 2024大会上&#xff0c;AMD推出Instinct MI325X AI加速器&#xff08;以下简称MI325X&#xff09;&#xff0c;直接与英伟达的Blackwell芯片正面交锋。 现场展示的数据显示&#xff0c;与英伟达H200的集成平台H200 …

【大数据学习 | Spark调优篇】Spark之内存调优

1. 内存的花费 1&#xff09;每个Java对象&#xff0c;都有一个对象头&#xff0c;会占用16个字节&#xff0c;主要是包括了一些对象的元信息&#xff0c;比如指向它的类的指针。如果一个对象本身很小&#xff0c;比如就包括了一个int类型的field&#xff0c;那么它的对象头实…