axios取消重復(fù)請(qǐng)求實(shí)現(xiàn)

在我們?nèi)粘i_發(fā)中,有這樣一種場(chǎng)景必須要進(jìn)行處理,那就是在提交表單的時(shí)候,如果很快的重復(fù)點(diǎn)擊兩次,會(huì)造成重復(fù)請(qǐng)求,第二次請(qǐng)求就會(huì)報(bào)錯(cuò),給用戶帶來(lái)很不好的體驗(yàn),同時(shí)如果后端沒(méi)有加以控制,也容易造成數(shù)據(jù)重復(fù)。所以我們需要對(duì)這種重復(fù)請(qǐng)求進(jìn)行處理和控制。

axios是現(xiàn)在前端項(xiàng)目開發(fā)中必用的一個(gè)用于前端后網(wǎng)絡(luò)請(qǐng)求的工具,它是基于ajaxpromise封裝而成,很受歡迎。

那么,今天就結(jié)合axios來(lái)實(shí)現(xiàn)一下如何取消重復(fù)請(qǐng)求。

思路整理

取消重復(fù)請(qǐng)求的思路就是,將每一次請(qǐng)求的url、method、paramsdata拼接起來(lái)組成一個(gè)key,然后添加到map中,下一次請(qǐng)求時(shí)就拿keymap中查找是否已存在,如果存在就表示重復(fù)請(qǐng)求,就取消,如果不存在就放行,等請(qǐng)求成功后在從map中刪除這個(gè)key。

axios中如何取消請(qǐng)求

  • 普通請(qǐng)求取消
    在普通的ajax中,是通過(guò)XMLHttpRequestabort()方法實(shí)現(xiàn)取消請(qǐng)求的。
var ajax = new XMLHttpRequest();
ajax.open('GET', '/abc');
ajax.send();
setTimeout(() => {
  ajax.abort(); // 取消當(dāng)前請(qǐng)求
}, 300);
  • axios請(qǐng)求取消
    axios中是通過(guò)CancelToken給每個(gè)請(qǐng)求添加一個(gè)cancelToken屬性,使得每個(gè)請(qǐng)求具備取消請(qǐng)求的能力。有兩種方法實(shí)現(xiàn):
// 方式一:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).then(res => {});

// cancel方法里面的參數(shù)是可選參數(shù)
source.cancel('取消請(qǐng)求');

// 方式二:
const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // 這里的c就是取消當(dāng)前請(qǐng)求的方法,這里把c賦值給cancel變量
    cancel = c;
  })
});
// 調(diào)用取消方法
cancel();

兩種實(shí)踐方式

  • 重復(fù)請(qǐng)求取消前一次
  1. 封裝取消重復(fù)請(qǐng)求類
// cancel-request.js
import qs from 'qs'
export default class CancelRequest {
  constructor() {
    this.pendingRequest = new Map()
  }
  // 根據(jù)請(qǐng)求信息生成唯一標(biāo)識(shí)key
  geterateReqKey(config) {
    const { url, method, params, data } = config
    return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
  }
  // 把當(dāng)前請(qǐng)求信息添加到pendingRequest對(duì)象中
  addPendingRequest(config, CancelToken) {
    const requestKey = this.geterateReqKey(config)
    config.cancelToken =
      config.cancelToken ||
      new CancelToken(cancel => {
        if (!this.pendingRequest.has(requestKey)) {
          // 把請(qǐng)求取消方法作為 map 值存起來(lái)
          this.pendingRequest.set(requestKey, cancel)
        }
      })
  }
  // 檢查是否存在重復(fù)請(qǐng)求,若存在則取消前一次請(qǐng)求
  removePendingRequest(config) {
    const requestKey = this.geterateReqKey(config)
    if (this.pendingRequest.has(requestKey)) {
      const cancel = this.pendingRequest.get(requestKey)
      // 取消請(qǐng)求
      cancel(requestKey)
      // 刪除map中對(duì)應(yīng)的屬性
      this.removeRequestKey(config)
    }
  }
  // 從pendingRequest中刪除對(duì)應(yīng)的key
  removeRequestKey(config) {
    const requestKey = this.geterateReqKey(config)
    this.pendingRequest.delete(requestKey)
  }
}
  1. axios攔截中使用
// request.js
import axios from 'axios';
import CancelRequest from './cancel-request.js'
// 實(shí)例化
let cancelRequest = new CancelRequest()
const instance = axios.create({
  // ...
});

// 請(qǐng)求攔截器
instance.interceptors.request.use(config => {
  // 在請(qǐng)求開始之前檢查先前的請(qǐng)求,如果是重復(fù)請(qǐng)求,刪除之前的
  cancelRequest.removePendingRequest(config);
  // 如果不存在就將當(dāng)前請(qǐng)求添加到pendingRequest
  cancelRequest.addPendingRequest(config);
    return config;
}, err => {
    Promise.reject(err);
});
// 響應(yīng)攔截器
instance.interceptors.response.use(res => {
  // 移除成功請(qǐng)求記錄
    cancelRequest.removeRequestKey(res.config)
    return res.data;
}, err => {
  // 失敗時(shí)也需要移除
    cancelRequest.removeRequestKey(err.config || {} )
    Promise.reject(err);
});
export default instance;

這種方式雖然是可以取消重復(fù)請(qǐng)求,但是瀏覽的network中取消的請(qǐng)求會(huì)顯示canceled狀態(tài),用戶是有感知的!于是,就有了第二種方式,取消第二次請(qǐng)求,并實(shí)現(xiàn)用戶無(wú)感。

  • 重復(fù)請(qǐng)求取消第二次
  1. 封裝取消重復(fù)請(qǐng)求類
import qs from 'qs'
export default class CancelRequest {
  constructor() {
    this.pendingRequest = new Map()
  }
  // 根據(jù)請(qǐng)求信息生成唯一標(biāo)識(shí)key
  geterateReqKey(config) {
    const { url, method, params, data } = config
    return [url, method, qs.stringify(params), qs.stringify(data)].join('&')
  }
  // 檢查是否是重復(fù)請(qǐng)求,如果是取消第二次
  checkoutPendingRequest(config, CancelToken) {
    // 為每個(gè)請(qǐng)求添加cancelToken,同時(shí)拿到source獲取到對(duì)每個(gè)請(qǐng)求取消請(qǐng)求的能力(cancel方法)
    let source = null
    if (config.cancelToken) {
      source = config.source
    } else {
      source = CancelToken.source()
      config.cancelToken = source.token
    }
    const requestKey = this.geterateReqKey(config)
    if (this.pendingRequest.has(requestKey)) {
      // 取消重復(fù)請(qǐng)求(第二次)
      source.cancel('double request:' + requestKey)
    } else {
      // 沒(méi)重復(fù)就添加
      this.pendingRequest.set(requestKey, source)
    }
  }
  // 從請(qǐng)求列表中刪除
  removeRequestKey(config) {
    // 延遲一點(diǎn)是為了避免用戶快速多次點(diǎn)擊提交,而第一次請(qǐng)求成功立刻清除掉,第二次請(qǐng)求不會(huì)被取消
    setTimeout(() => {
      const requestKey = this.geterateReqKey(config)
      this.pendingRequest.delete(requestKey)
    }, 200)
  }
}
    1. 在axios攔截中使用
// request.js
import axios from 'axios';
import CancelRequest from './cancel-request.js'
// 實(shí)例化
let cancelRequest = new CancelRequest()
const instance = axios.create({
  // ...
});

// 請(qǐng)求攔截器
instance.interceptors.request.use(config => {
// 檢查之前是否存在相同的請(qǐng)求,如果存在則取消。不存在就記錄
  cancelRequest.checkoutPendingRequest(config);
    return config;
}, err => {
    Promise.reject(err);
});
// 響應(yīng)攔截器
instance.interceptors.response.use(res => {
  // 移除成功請(qǐng)求記錄
    cancelRequest.removeRequestKey(res.config)
    return res.data;
}, err => {
    Promise.reject(err);
});
export default instance;

至此,就分享完畢,希望大家有所獲益!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容