Axios取消请求

打印 上一主题 下一主题

主题 796|帖子 796|积分 2388

1. 问题场景

有两个tab:tab A 和tab B。如今点击A会发送A请求,点击B会发送B请求。如果用户点击A之后快速点击B,A请求比B请求先返回,那么页面就会错误渲染A。AB请求返回的数据效果完全一致,只是内容不同,在这种情况下,如何表现B请求的数据而不是A请求的数据。
本地服务器预备
  1. const http = require('http')
  2. const url = require('url')
  3. const server = http.createServer()
  4. server.on('request', (req, res) => {
  5.     console.log('Someone visit our web server')
  6.     const method = req.method
  7.     const URL = req.url
  8.     console.log(`Your request url is ${URL}, and request method is ${method}`)
  9.     if (req.method === 'OPTIONS') {
  10.         console.log('OPTIONS')
  11.         res.writeHead(200, {
  12.           'Access-Control-Allow-Origin': 'http://127.0.0.1:8080',
  13.           'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With, x-custom-header',
  14.           'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS',
  15.           'Access-Control-Allow-Credentials': 'true'
  16.         })
  17.         res.end()
  18.         return // 确保在 OPTIONS 请求中及时返回,不再继续处理请求
  19.     }
  20.     if (method === 'GET') {
  21.         const { query } = url.parse(URL)
  22.         const params = {}
  23.         if (query.includes('&')) {
  24.             query.split('&').forEach(item => {
  25.                 const queryPair = item.split('=')
  26.                 params[queryPair[0]] = queryPair[1]
  27.             })
  28.         } else {
  29.             const queryPair = query.split('=')
  30.             params[queryPair[0]] = queryPair[1]
  31.         }
  32.         const tab = params['tab']
  33.         // 设置跨域响应头
  34.         res.writeHead(200, {
  35.             'Content-Type': 'application/json',
  36.             'Access-Control-Allow-Origin': 'http://127.0.0.1:8080',
  37.             'Access-Control-Allow-Headers': 'Content-Type, Authorization, Content-Length, X-Requested-With',
  38.             'Access-Control-Allow-Methods': 'PUT, POST, GET, DELETE, OPTIONS',
  39.             'Access-Control-Allow-Credentials': 'true' // 添加此行
  40.         })
  41.         // 把内容发送给客户端
  42.         res.end(JSON.stringify(`Now it is the content of tab ${tab}`))
  43.     }
  44. })
  45. server.listen(8081, () => {
  46.     console.log('server running at http://127.0.0.1:3000')
  47. })
复制代码
HTML文件预备
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>cancelDemo</title>
  7.     <style>
  8.         .box {
  9.             position: relative;
  10.             margin: 0 auto;
  11.             margin-top: 60px;
  12.             width: 400px;
  13.             height: 540px;
  14.             border: 2px solid #000;
  15.             border-radius: 10px;
  16.         }
  17.         .box .content {
  18.             margin: 20px;
  19.             width: 360px;
  20.             height: 400px;
  21.             border: 2px solid #efefef;
  22.             border-radius: 10px;
  23.             text-align: center;
  24.         }
  25.         .box .tab {
  26.             position: absolute;
  27.             display: flex;
  28.             justify-content: space-evenly;
  29.             align-items: center;
  30.             bottom: 0;
  31.             width: 100%;
  32.             height: 60px;
  33.             border-top: 2px solid #9a9da3;
  34.             border-radius: 5px;
  35.         }
  36.         .box .tab .btn {
  37.             padding: 10px 20px;
  38.             height: 40px;
  39.             background-color: #4479e1;
  40.             box-sizing: border-box;
  41.             border-radius: 10px;
  42.         }
  43.     </style>
  44. </head>
  45. <body>
  46.     <div class="box">
  47.         <div class="content">Now it is the content of tab 1</div>
  48.         <div class="tab">
  49.             <div class="btn" data-index="1">tab 1 </div>
  50.             <div class="btn" data-index="2">tab 2 </div>
  51.             <div class="btn" data-index="3">tab 3 </div>
  52.         </div>
  53.     </div>
  54.     <!-- <script src="./index.js" type="module"></script> -->
  55.     <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  56.     <script type="module" src="./index.js"></script>
  57. </body>
  58. </html>
复制代码
index.js文件预备
  1. const tab = document.querySelector('.tab')
  2. tab.addEventListener('click', e => {
  3.   if (e.target.className !== 'btn') return
  4.   const index = e.target.dataset.index
  5.   axios({
  6.     method: 'GET',
  7.     url: `http://127.0.0.1:3000/index?tab=${index}`
  8.   }).then(res => {
  9.     document.querySelector('.content').innerHTML = res.data
  10.   })
  11. })
复制代码
2. Axios方法

Axios官方提供了两种取消请求的方法:

  • 使用 cancel token 取消一个请求(该方法的cancel token API 在 v0.22.0 中已经被废弃,以是不推荐使用)
    使用 CancelToken.source 工厂方法创建一个 cancel token
    1. const CancelToken = axios.CancelToken;
    2. const source = CancelToken.source();
    3. axios.get('/user/12345', {
    4.   cancelToken: source.token
    5. }).catch(function (thrown) {
    6.   if (axios.isCancel(thrown)) {
    7.     console.log('Request canceled', thrown.message);
    8.   } else {
    9.     // 处理错误
    10.   }
    11. });
    12. axios.post('/user/12345', {
    13.   name: 'new name'
    14. }, {
    15.   cancelToken: source.token
    16. })
    17. // 取消请求(message 参数是可选的)
    18. source.cancel('Operation canceled by the user.');
    复制代码
    也可以通过通报一个 executor 函数到 CancelToken 的构造函数来创建一个 cancel token
    1. const CancelToken = axios.CancelToken;
    2. let cancel;
    3. axios.get('/user/12345', {
    4.   cancelToken: new CancelToken(function executor(c) {
    5.     // executor 函数接收一个 cancel 函数作为参数
    6.     cancel = c;
    7.   })
    8. });
    9. // 取消请求
    10. cancel();
    复制代码
  • Axios 支持以 fetch API 方式 —— AbortController 取消请求
    1. const controller = new AbortController();
    2. axios.get('/foo/bar', {
    3.    signal: controller.signal
    4. }).then(function(response) {
    5.    //...
    6. });
    7. // 取消请求
    8. controller.abort()
    复制代码
3. 取消请求实战

先对axios进行二次封装,这里简单地设置baseURL和timeout。下面是request中的index.js
  1. // request/index.js
  2. export const server = axios.create({
  3.     baseURL: 'http://127.0.0.1:8081/',
  4.     timeout: 3000,
  5.     headers: {
  6.         'X-Custom-Header': 'foobar',
  7.     }
  8. })
复制代码
再对获取tab信息的api进行封装:
  1. // request/tab.js
  2. import {server} from './index.js'
  3. // 切换tab的请求
  4. export const getTabContentAPI = async (index, cfg) => {
  5.     cfg = cfg || {}
  6.     return await server.get(`index?tab=${index}`, cfg)
  7. }
复制代码
对tab请求增加取消功能时,必要在调用api时,加上cancelToken选项
  1. import { getTabContentAPI } from "./request/tab.js"
  2. const tab = document.querySelector('.tab')
  3. const CancelToken = axios.CancelToken
  4. let cancelFn
  5. const getTabContent = async index => {
  6.   if (index == 3) cancelFn(`取消了tab${index}的上一个请求`)
  7.   const res = await getTabContentAPI(index, {
  8.     cancelToken:  new CancelToken(c => cancelFn = c)
  9.   })
  10.   return res
  11. }
  12. tab.addEventListener('click', async e => {
  13.   if (e.target.className !== 'btn') return
  14.   const index = e.target.dataset.index
  15.   const res = await getTabContent(index)
  16.   document.querySelector('.content').innerHTML = res.data
  17. })
复制代码
但如许只能做到取消tab3的前一个请求,无法灵活地做到取消每一个tab的前一个请求。同时如果直接使用 cancelFn(取消了tab${index}的上一个请求) ,就会导致首次发送请求时, cancelFn 还不是function。以是我们可以用一个数组来生存cancelFn,同时使用try catch
  1. // index.js
  2. import { getTabContentAPI } from "./request/tab.js"
  3. const tab = document.querySelector('.tab')
  4. const CancelToken = axios.CancelToken
  5. const isCancel = axios.isCancel
  6. let cancelFnArr = []
  7. const getTabContent = async index => {
  8.   if (cancelFnArr.length > 0) {
  9.     cancelFnArr.pop()(`取消了tab${index}的上一个请求`)
  10.   }
  11.   const res = await getTabContentAPI(index, {
  12.     cancelToken:  new CancelToken(c => cancelFnArr.push(c))
  13.   })
  14.   return res
  15. }
  16. tab.addEventListener('click', async e => {
  17.   if (e.target.className !== 'btn') return
  18.   const index = e.target.dataset.index
  19.   try {
  20.     const res = await getTabContent(index)
  21.     document.querySelector('.content').innerHTML = res.data
  22.   } catch(err) {
  23.     if (isCancel(err)) {
  24.       console.log('请求被取消', err.message)
  25.     } else {
  26.       console.log('请求出错', err.message)
  27.     }
  28.   }
  29.   
  30.   
  31. })
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表