如何封装一个可取消的 HTTP 请求?

前言

你可能会好奇什么样的场景会需要取消 HTTP 请求呢?

确实在实际的项目开发中,可能会很少有这样的需求,但是不代表没有,比如:

假如要实现上述这个公告栏,每点击一个 tab 按钮就会切换展示容器容器中的内容,但是由于这是三个 tab 按钮对应展示容器和信息条目结构样式都一致,于是为了 复用这个结构,将展示容器和其中的信息条目都作为三个 tab 按钮对应展示效果,只是点击每个 tab 按钮后 发送请求 后得到的数据不同.

正常情况下 tab1 对应的数据在初始化时进行展示,但是现在还存在一个问题,那就是如果点击完 tab2 并且已经发送了请求获取数据,假设这个请求需要 30s 后才响应回来,由于用户比较着急于是直接点了 tab3 ,此时 tab3 对应的接口已经发送出去但是未响应,而此时 tab2 对应的接口响应回来了,就会导致展示容器中的信息条目展现了错误的数据.

现在你应该知道,为什么需要一个可取消的 HTTP 请求了吧!!

准备工作

为了避免 端口、协议、IP 等不一致产生的跨域问题,这里就直接使用 express 启动一个本地服务,并返回测试页面 index.html,目录结构如下:

image.png

  • 其中最外层的 serverv.js 就是本地服务的代码,内容很简单:
    const path = require('path')
    const express = require('express')
    const app = express()// 返回静态资源
    app.use(express.static(path.join(__dirname, 'src')))// 处理 api 请求接口
    app.get('/message', function (req, res) {setTimeout(()=>{res.send('Hello World')} , 1000);
    });app.listen(3000, (error) => {if (error) {console.error("server error:", error);return}console.log("server runing at port 3000 ...");
    })
    
  • src/index.html 是测试页面,里面引入 src/request.js 中封装的请求方法
  • src/request.js 简单的封装了一些请求方法,包含 XMLHttpRequestfetchaxios 三种方式,内容如下:
    // xhrRequest
    function xhrRequest({method = 'get',url,params = null,async = true,success,
    }) {success = typeof success === 'function' ? success : () => {}const xhr = new XMLHttpRequest()xhr.open(method, url, async)xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {success(xhr.responseText)} else {console.log('Request was unsuccessful: ' + xhr.status)}}}method === 'post' ? xhr.send(params || null) : xhr.send()return () => {console.log(`当前请求已被取消,url:`, url)xhr.abort()}
    }// fetchRequest
    function fetchRequest({ method = 'GET', url, params }) {let abortController = new AbortController()let patload = {method: method,signal: abortController.signal,}if (method === 'POST') patload.body = JSON.stringify(params)return {abort: () => {console.log(`当前请求已被取消,url:`, url)abortController.abort()},result: fetch(url, patload),}
    }// axiosRequest
    window.axiosCancelArr = []
    let Axios = null
    function axiosRequest() {if (Axios) return AxiosAxios = axios.create({baseURL: '',timeout: 10000,})//请求前拦截Axios.interceptors.request.use((config) => {// 添加取消标记config.cancelToken = new axios.CancelToken((cancel) => {window.axiosCancelArr.push({url: config.url, cancel})})return config},(error) => {return Promise.reject(error)},)//请求后返回数据拦截Axios.interceptors.response.use((res) => {return res},(res) => {return Promise.reject(res)},)return Axios
    }
    

原生方法取消请求

XMLHttpRequest —— (new XMLHttpRequest()).abort()

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

  <div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button></div><script src="./request.js"></script><script>let cancel = () => {}function send(){console.log("正在发送 xhr 请求...")// 获取取消请求的方法cancel = xhrRequest({url:'/message',success(data){console.log("接收数据:", data)}})}<script>

fetch —— (new AbortController()).abort()

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {}// fetchfunction send() {console.log('正在发送 fetch 请求...')const { result, abort } = fetchRequest({url: '/message',})cancel = abortresult.then((data) => {console.log('接收数据:', data)}).catch((error) => {console.log('fetch error:', error)})}</script>

axios —— new axios.CancelToken((cancel) => {}))

下面的图示,演示了 请求成功请求被取消 时的两种表现:

以下是在测试页面 index.html 中的代码,对应 js 代码在准备工作部分:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {}// axiosconst AxiosInstance = axiosRequest()function send() {console.log('正在发 axios 送请求...')AxiosInstance.get('/message').then((res) => {console.log('接收数据:', res)}).catch((error) => {console.log('axios error:', error)})}cancel = () => {console.log('axiosCancel = ', window.axiosCancel)window.axiosCancelArr.forEach((item) => {item.cancel()})}
</script>

实现自定义方法 " 取消请求 "

注意这里的 “请求方法” 是包含引号的,也就是说并不是像原生方法那样真的把已发送出去的请求进行取消,但是可以通过其他方式不在接收之前请求响应的数据,转而使用最新接口响应的数据,从侧面实现避免旧接口响应造成的干扰.

如果要自定义实现 “取消请求” 方法,最先想到的应该是 promise,因为 promise 有一个重要的特性就是:状态一旦从 pending -> fullfilled 或 pending -> rejected 的变更,之后就无法在进行更改.

既然如此,我们就可以在接口响应时通过 promise.resolve() 进行返回,一旦需要 “取消请求” 就可以先于接口响应时先更改对应 promise 的状态,从而抛弃上一次接口响应的数据.

request.js 中代码如下:

// xhrRequestPromise
window.cancelXhrRequestArr = []
function xhrRequestPromise({method = 'get',url,params = null,async = true,
}) {return new Promise((resolve, reject) => {cancelXhrRequestArr.push({ reject, url })success = typeof success === 'function' ? success : () => {}const xhr = new XMLHttpRequest()xhr.open(method, url, async)xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {resolve(xhr.responseText)} else {console.log('Request was unsuccessful: ' + xhr.status)}}}method === 'post' ? xhr.send(params || null) : xhr.send()})
}

测试页面 index.html 代码如下:

<div><button onclick="send()">send request</button><button onclick="cancel()">cancel request</button>
</div><script src="./request.js"></script>
<script>let cancel = () => {cancelXhrRequestArr.forEach(({reject, url}) => {reject(`abandon request url:${url}`)})}// xhrRequestPromisefunction send() {console.log('正在发送 xhr 请求...')const xhrp = xhrRequestPromise({ url: '/message' }).then((res) => {console.log('接收数据:', res)}).catch((error) => {console.log('xhrRequest error:', error)})}</script>

最后

上面通过 XMLHttpRequestfetchaxios 三种方式来演示了如何取消请求,同时也通过 promise 实现了自定义 “取消请求” 的方法,简单来说,通过原生方法就是把这个请求给取消了,自定义实现方法就是把将旧数据直接抛弃不适用,明白了这一点其实通过其他的方式实现也可以.

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端笔试新问题总结

记录总结下最近遇到的前端笔试新问题 目录 一、操作数组方法 1.Array.isArray(arr) 2.Object.prototype.toString.call(arr) "[object Array]" 3.arr instanceof Array 1&#xff09;跨帧问题 2&#xff09;修改Array.prototype 3&#xff09;模拟数组的对象…

玩转Hugging Face/魔搭社区/魔乐社区”教程

2.1 HF 平台 2.1.1 注册Hugging Face 平台 &#xff08;需要魔法上网&#xff09; Hugging Face 最初专注于开发聊天机器人服务。尽管他们的聊天机器人项目并未取得预期的成功&#xff0c;但他们在GitHub上开源的Transformers库却意外地在机器学习领域引起了巨大轰动。如今&…

Chrome与夸克谁更节省系统资源

在当今数字化时代&#xff0c;浏览器已经成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;我们都依赖于浏览器来访问互联网。然而&#xff0c;不同的浏览器在性能和资源消耗方面存在差异。本文将探讨Chrome和夸克两款浏览器在系统资源消耗方面的表现…

【OJ题解】C++实现反转字符串中的每个单词

&#x1f4b5;个人主页: 起名字真南 &#x1f4b5;个人专栏:【数据结构初阶】 【C语言】 【C】 【OJ题解】 题目要求&#xff1a;给定一个字符串 s &#xff0c;你需要反转字符串中每个单词的字符顺序&#xff0c;同时仍保留空格和单词的初始顺序。 题目链接: 反转字符串中的所…

Vue 学习随笔系列十三 -- ElementUI 表格合并单元格

ElementUI 表格合并单元格 文章目录 ElementUI 表格合并单元格[TOC](文章目录)一、表头合并二、单元格合并1、示例代码2、示例效果 一、表头合并 参考&#xff1a; https://www.jianshu.com/p/2befeb356a31 二、单元格合并 1、示例代码 <template><div><el-…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)4.3-4.4

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第四周 特殊应用&#xff1a;人脸识别和神经风格转换&#xff08;Special applications: Face recognition &Neural style transfer&#xff09;4.3 Siamese 网络&#xff08;Siamese net…

vue data变量之间相互赋值或进行数据联动

摘要&#xff1a; 使用vue时开发会用到data中是数据是相互驱动&#xff0c;经常会想到watch,computed&#xff0c;总结一下&#xff01; 直接赋值&#xff1a; 在 data 函数中定义的变量可以直接在方法中进行赋值。 export default {data() {return {a: 1,b: 2};},methods: {u…

安装Blender并使用

前言 该系列记录了如何用Blenderpro来构建自己的场景数据集&#xff0c;从环境搭建到后期构建数据集的整个流程 本文章是第一部分&#xff0c;BlenderPrc2的安装以及环境配置 部分参考https://blog.csdn.net/weixin_49521551/article/details/121573334 官方文档https://dlr…

百度SEO分析实用指南 提升网站搜索排名的有效策略

内容概要 在数字化时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已经成为提升网站曝光度的关键工具。本指南将带您了解SEO的基本知识&#xff0c;帮助您在复杂的网络环境中立足。我们将从关键词优化开始&#xff0c;重点讲解如何选择合适的关键词来提高搜索引擎排…

【蔬菜识别】Python+深度学习+CNN卷积神经网络算法+TensorFlow+人工智能+模型训练

一、介绍 蔬菜识别系统&#xff0c;本系统使用Python作为主要编程语言&#xff0c;通过收集了8种常见的蔬菜图像数据集&#xff08;‘土豆’, ‘大白菜’, ‘大葱’, ‘莲藕’, ‘菠菜’, ‘西红柿’, ‘韭菜’, ‘黄瓜’&#xff09;&#xff0c;然后基于TensorFlow搭建卷积神…

ESP8266 自定义固件烧录-Tcpsocket固件

一、固件介绍 固件为自定义开发的一个适配物联网项目的开源固件&#xff0c;支持网页配网、支持网页tcpsocket服务器配置、支持串口波特率设置。 方便、快捷、稳定&#xff01; 二、烧录说明 固件及工具打包下载地址&#xff1a; https://download.csdn.net/download/flyai…

探秘机器学习算法:智慧背后的代码逻辑

1、 线性回归 线性回归是预测连续变量的一种简单而有效的方法。其数学模型假设因变量 y 与自变量 x 之间存在线性关系&#xff0c;用公式表示为&#xff1a; ​ Python代码实现 import numpy as np from sklearn.linear_model import LinearRegression import matplotlib.…

【合肥工业大学】操作系统 习题解析 作业答案(仅作学习与交流/侵删)

第一章习题解析 1&#xff0e;设计现代OS的主要目标是什么&#xff1f; 答&#xff1a;&#xff08;1&#xff09;有效性 &#xff08; 2&#xff09;方便性 &#xff08; 3&#xff09;可扩充性 &#xff08; 4&#xff09;开放性 2&#xff0e; OS 的作用可表现在哪几个方…

要在微信小程序中让一个 `view` 元素内部的文字水平垂直居中,可以使用 Flexbox 布局

文章目录 主要特点&#xff1a;基本用法&#xff1a;常用属性&#xff1a; 要在微信小程序中让一个 view 元素内部的文字水平垂直居中&#xff0c;可以使用 Flexbox 布局。以下是如何设置样式的示例&#xff1a; .scan-button {display: flex; /* 启用 Flexbox 布局 */justify…

JSON交互处理

目录 一、什么是JSON 二、JSON和JavaScript对象互转 ​三、Controller返回JSON数据 3.1 使用Jackson 编写Controller 1. 一个对象 2. 多个对象 3. 输出时间对象 4. 优化&#xff1a;抽取为工具类 一、什么是JSON Json是JavaScript对象的字符串表示法&#xff0c;它用…

WPF+MVVM案例实战(十二)- 3D数字翻牌计时实现

文章目录 1、运行效果2、功能实现1、文件创建2、控件代码实现3、控件引用与菜单实现1.引用用户控件2.按钮菜单3、计时器界面实现4、源代码获取1、运行效果 3D数字翻牌计时 2、功能实现 1、文件创建 打开项目 Wpf_Examples ,在用户控件 UserControlLib 中创建 NumberFoldi…

无人机多机编队控制算法详解!

一、主要算法类型 长机-僚机法&#xff08;Leader-Follower&#xff09; 原理&#xff1a;通过设定一架无人机作为长机&#xff08;领航者&#xff09;&#xff0c;其他无人机作为僚机&#xff08;跟随者&#xff09;&#xff0c;僚机根据长机的信息来调整自身的飞行状态&#…

RL学习笔记-表格型方法

参考资料&#xff1a;蘑菇书&#xff1b;《世界冠军带你从零实践强化学习》B站课程 Q表格 前面讲马尔可夫过程的时候提到过Q函数&#xff0c;Q函数是在某状态s下采取某动作a得到的平均奖励&#xff08;状态动作价值&#xff09;。Q表格就是在状态和动作两个维度上可视化的一张二…

基于SSM+微信小程序的订餐管理系统(点餐2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的订餐管理系统实现了管理员和用户。管理端实现了 首页、个人中心、用户管理、菜品分类管理、菜品信息管理、订单信息管理、配送信息管理、菜品评价管理、订单投诉管理、…

太速科技-712-6U VPX飞腾处理器刀片计算机

6U VPX飞腾处理器刀片计算机 一、产品概述 该产品是一款基于国产飞腾FT-2000四核处理器或D2000八核处理器的高性能6U VPX刀片式计算机。产品提供了可支持全网状交换的高速数据通道&#xff0c;其中P1、P2均支持1个PCIe x16 Gen3或2个PCIe x8 Gen3或4个PCIe x4 Gen3总…