首页 > 其他分享 >如何封装一个可取消的 HTTP 请求?

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

时间:2024-10-29 09:48:02浏览次数:7  
标签:HTTP 封装 请求 url xhr error console log

前言

你可能会好奇什么样的场景会需要取消 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 Axios
    
     Axios = 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 = () => {}

  // fetch
  function send() {
    console.log('正在发送 fetch 请求...')
    const { result, abort } = fetchRequest({
      url: '/message',
    })

    cancel = abort

    result
      .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 = () => {}

  // axios
  const 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}`)
    })
  }

  // xhrRequestPromise
  function 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,封装,请求,url,xhr,error,console,log
From: https://blog.csdn.net/weixin_43822185/article/details/143320886

相关文章

  • httpsok:自动续期SSL证书的最佳选择!
    一、引言        在数字化时代,网站的安全性至关重要,而SSL证书是保护用户数据、提升网站信誉的关键。然而,证书的续期往往令人头痛。今天,我们为你介绍一款高效的SSL证书自动续期工具——httpsok,让你的证书管理变得轻松无忧。二、什么是httpsok?httpsok是一款专为网站......
  • nginx 根路径同时代理 http ws sse 三种请求
    HTTP(HyperTextTransferProtocol):超文本传输协议,是用于在客户端(通常是web浏览器)和服务器之间传输数据的协议。HTTP是Web的基础,用于请求和传输网页、图像、视频等资源。它采用请求-响应模型,支持多种方法(如GET、POST等),并可通过HTTP/1.1和HTTP/2等版本进行优化,以提高性能和用户体验。......
  • 【揭秘】Logback日志如何实现请求唯一追踪ID,提升系统监控效能!
    在分布式系统中,为了方便追踪和调试问题,通常会为每个请求生成一个唯一的追踪ID(TraceID)。这个ID可以在整个请求的生命周期中传递,并在日志中记录。Logback是一个流行的Java日志框架,可以通过自定义MDC(MappedDiagnosticContext)来实现这一功能。以下是如何在Logback中添......
  • GBPC3510-ASEMI整流桥GBPC3510参数、封装、尺寸
    编辑:llGBPC3510-ASEMI整流桥GBPC3510参数、封装、尺寸型号:GBPC3510品牌:ASEMI封装:GBPC-4批号:2024+现货:50000+最大重复峰值反向电压:1000V最大正向平均整流电流(Vdss):35A功率(Pd):大功率芯片个数:4引脚数量:4安装方式:插件类型:插件方桥、整流桥正向浪涌电流IFSM:400A正向电......
  • 如何设置Kubernetes的资源限制和请求
    开头段落:在Kubernetes中设置资源限制和请求对于确保集群内应用可靠运行以及合理分配资源至关重要。资源请求用于告知Kubernetes调度器该为Pod预留多少资源,资源限制则指定Pod可以使用的最大资源量,确保单个应用不会消耗过多资源而影响集群稳定性。理解并正确设置资源请求和限制可以......
  • HTTP相关返回值异常如何解决(上篇)
    ​今天我们讲讲HTTP相关返回值异常如何解决(实例持续更新中)一、HTTP介绍HTTP(超文本传输协议,HypertextTransferProtocol)是用于在网络上进行数据交换的应用层协议。它是万维网(WWW)的基础,允许客户端(通常是网页浏览器)与服务器之间进行通信。以下是对HTTP的一些基本介绍:基本概......
  • [笔记] SpringBoot3 使用 EasyExcel 封装工具类实现 自定义表头 导出并实现 数据格式
    在现代企业应用中,数据导出功能是非常常见的需求。特别是在处理大量数据时,将数据导出为Excel文件不仅方便用户查看和分析,还能提高数据处理的效率。ApachePOI是一个常用的JavaExcel处理库,但它在处理大数据量时性能较差。为此,阿里巴巴开源了EasyExcel,这是一个基于Java......
  • 【Nginx系列】关于一次请求超时的思考
    ......
  • Watt Toolkit 报错:加速服务启动失败,443端口被 svnhttpsvc (4996) 占用
    问题描述WattToolkit(原名Steam++)启动加速时报错,显示443端口被svnhttpsvc(4996)占用了。svnhttpsvc是VisualSVNServer的一个应用程序,使用HTTPS协议,默认端口为443。在任务管理器(桌面底部任务栏右键打开)中搜索进程svnhttpsvc的PID4996可以看到svnhttpsvc是正......
  • 局域网使用mkcert配置服务的https访问
    制作ssl证书下载mkcert下载链接:https://github.com/FiloSottile/mkcert/releases安装mkcert(windows为例),生成CA证书下载完成后,在下载目录打开命令行,输入mkcert-install安装CA证书生成自签证书mkcert192.168.2.64或者mkcertexample.com生成自签证书给客户端安装C......