1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【實戰(zhàn)】Axios 如何取消重復(fù)請求?

        共 7619字,需瀏覽 16分鐘

         ·

        2021-04-27 00:51

        在 Web 項目開發(fā)過程中,我們經(jīng)常會遇到重復(fù)請求的場景,如果系統(tǒng)不對重復(fù)的請求進(jìn)行處理,則可能會導(dǎo)致系統(tǒng)出現(xiàn)各種問題。比如重復(fù)的 post 請求可能會導(dǎo)致服務(wù)端產(chǎn)生兩筆記錄。那么重復(fù)請求是如何產(chǎn)生的呢?這里我們舉 2 個常見的場景:

        • 假設(shè)頁面中有一個按鈕,用戶點擊按鈕后會發(fā)起一個 AJAX 請求。如果未對該按鈕進(jìn)行控制,當(dāng)用戶快速點擊按鈕時,則會發(fā)出重復(fù)請求。
        • 假設(shè)在考試結(jié)果查詢頁面中,用戶可以根據(jù) “已通過”、“未通過” 和 “全部” 3 種查詢條件來查詢考試結(jié)果。如果請求的響應(yīng)比較慢,當(dāng)用戶在不同的查詢條件之前快速切換時,就會產(chǎn)生重復(fù)請求。

        既然已經(jīng)知道重復(fù)請求是如何產(chǎn)生的,也知道了它會帶來一些問題。接下來,阿寶哥將以 Axios 為例,帶大家來一起解決重復(fù)請求的問題。

        一、如何取消請求

        Axios 是一個基于 Promise 的 HTTP 客戶端,同時支持瀏覽器和 Node.js 環(huán)境。它是一個優(yōu)秀的 HTTP 客戶端,被廣泛地應(yīng)用在大量的 Web 項目中。對于瀏覽器環(huán)境來說,Axios 底層是利用 XMLHttpRequest 對象來發(fā)起 HTTP 請求。如果要取消請求的話,我們可以通過調(diào)用 XMLHttpRequest 對象上的 abort 方法來取消請求:

        let xhr = new XMLHttpRequest();
        xhr.open("GET""https://developer.mozilla.org/"true);
        xhr.send();
        setTimeout(() => xhr.abort(), 300);

        而對于 Axios 來說,我們可以通過 Axios 內(nèi)部提供的 CancelToken 來取消請求:

        const CancelToken = axios.CancelToken;
        const source = CancelToken.source();

        axios.post('/user/12345', {
          name'semlinker'
        }, {
          cancelToken: source.token
        })

        source.cancel('Operation canceled by the user.'); // 取消請求,參數(shù)是可選的

        此外,你也可以通過調(diào)用 CancelToken 的構(gòu)造函數(shù)來創(chuàng)建 CancelToken,具體如下所示:

        const CancelToken = axios.CancelToken;
        let cancel;

        axios.get('/user/12345', {
          cancelTokennew CancelToken(function executor(c{
            cancel = c;
          })
        });

        cancel(); // 取消請求

        現(xiàn)在我們已經(jīng)知道在 Axios 中如何使用 CancelToken 來取消請求了,那么 CancelToken 內(nèi)部是如何工作的呢?這里我們先記住這個問題,后面阿寶哥將為你們揭開 CancelToken 背后的秘密。接下來,我們來分析一下如何判斷重復(fù)請求。

        二、如何判斷重復(fù)請求

        當(dāng)請求方式、請求 URL 地址和請求參數(shù)都一樣時,我們就可以認(rèn)為請求是一樣的。因此在每次發(fā)起請求時,我們就可以根據(jù)當(dāng)前請求的請求方式、請求 URL 地址和請求參數(shù)來生成一個唯一的 key,同時為每個請求創(chuàng)建一個專屬的 CancelToken,然后把 key 和 cancel 函數(shù)以鍵值對的形式保存到 Map 對象中,使用 Map 的好處是可以快速的判斷是否有重復(fù)的請求:

        import qs from 'qs'

        const pendingRequest = new Map();
        // GET -> params;POST -> data
        const requestKey = [method, url, qs.stringify(params), qs.stringify(data)].join('&'); 
        const cancelToken = new CancelToken(function executor(cancel{
          if(!pendingRequest.has(requestKey)){
            pendingRequest.set(requestKey, cancel);
          }
        })

        當(dāng)出現(xiàn)重復(fù)請求的時候,我們就可以使用 cancel 函數(shù)來取消前面已經(jīng)發(fā)出的請求,在取消請求之后,我們還需要把取消的請求從 pendingRequest 中移除?,F(xiàn)在我們已經(jīng)知道如何取消請求和如何判斷重復(fù)請求,下面我們來介紹如何取消重復(fù)請求。

        三、如何取消重復(fù)請求

        因為我們需要對所有的請求都進(jìn)行處理,所以我們可以考慮使用 Axios 的攔截器機(jī)制來實現(xiàn)取消重復(fù)請求的功能。Axios 為開發(fā)者提供了請求攔截器和響應(yīng)攔截器,它們的作用如下:

        • 請求攔截器:該類攔截器的作用是在請求發(fā)送前統(tǒng)一執(zhí)行某些操作,比如在請求頭中添加 token 字段。
        • 響應(yīng)攔截器:該類攔截器的作用是在接收到服務(wù)器響應(yīng)后統(tǒng)一執(zhí)行某些操作,比如發(fā)現(xiàn)響應(yīng)狀態(tài)碼為 401 時,自動跳轉(zhuǎn)到登錄頁。

        3.1 定義輔助函數(shù)

        在配置請求攔截器和響應(yīng)攔截器前,阿寶哥先來定義 3 個輔助函數(shù):

        • generateReqKey:用于根據(jù)當(dāng)前請求的信息,生成請求 Key;
        function generateReqKey(config{
          const { method, url, params, data } = config;
          return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
        }
        • addPendingRequest:用于把當(dāng)前請求信息添加到pendingRequest對象中;
        const pendingRequest = new Map();
        function addPendingRequest(config{
          const requestKey = generateReqKey(config);
          config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
            if (!pendingRequest.has(requestKey)) {
               pendingRequest.set(requestKey, cancel);
            }
          });
        }
        • removePendingRequest:檢查是否存在重復(fù)請求,若存在則取消已發(fā)的請求。
        function removePendingRequest(config{
          const requestKey = generateReqKey(config);
          if (pendingRequest.has(requestKey)) {
             const cancelToken = pendingRequest.get(requestKey);
             cancelToken(requestKey);
             pendingRequest.delete(requestKey);
          }
        }

        創(chuàng)建好 generateReqKeyaddPendingRequestremovePendingRequest 函數(shù)之后,我們就可以設(shè)置請求攔截器和響應(yīng)攔截器了。

        3.2 設(shè)置請求攔截器

        axios.interceptors.request.use(
          function (config{
            removePendingRequest(config); // 檢查是否存在重復(fù)請求,若存在則取消已發(fā)的請求
            addPendingRequest(config); // 把當(dāng)前請求信息添加到pendingRequest對象中
            return config;
          },
          (error) => {
             return Promise.reject(error);
          }
        );

        3.3 設(shè)置響應(yīng)攔截器

        axios.interceptors.response.use(
          (response) => {
             removePendingRequest(response.config); // 從pendingRequest對象中移除請求
             return response;
           },
           (error) => {
              removePendingRequest(error.config || {}); // 從pendingRequest對象中移除請求
              if (axios.isCancel(error)) {
                console.log("已取消的重復(fù)請求:" + error.message);
              } else {
                // 添加異常處理
              }
              return Promise.reject(error);
           }
        );

        由于完整的示例代碼內(nèi)容比較多,阿寶哥就不放具體的代碼了。感興趣的小伙伴,可以訪問以下地址瀏覽示例代碼。

        完整的示例代碼:https://gist.github.com/semlinker/e426780664f0186db434882f1e27ac3a

        這里我們來看一下 Axios 取消重復(fù)請求示例的運行結(jié)果:

        從上圖可知,當(dāng)出現(xiàn)重復(fù)請求時,之前已發(fā)送且未完成的請求會被取消掉。下面我們用一張流程圖來總結(jié)一下取消重復(fù)請求的處理流程:

        最后,我們來回答前面留下的問題,即 CancelToken 內(nèi)部是如何工作的?

        四、CancelToken 的工作原理

        在前面的示例中,我們是通過調(diào)用 CancelToken 構(gòu)造函數(shù)來創(chuàng)建 CancelToken 對象:

        new axios.CancelToken((cancel) => {
          if (!pendingRequest.has(requestKey)) {
            pendingRequest.set(requestKey, cancel);
          }
        })

        所以接下來,我們來分析 CancelToken 構(gòu)造函數(shù),該函數(shù)被定義在 lib/cancel/CancelToken.js 文件中:

        // lib/cancel/CancelToken.js
        function CancelToken(executor{
          if (typeof executor !== 'function') {
            throw new TypeError('executor must be a function.');
          }

          var resolvePromise;
          this.promise = new Promise(function promiseExecutor(resolve{
            resolvePromise = resolve;
          });

          var token = this;
          executor(function cancel(message// 設(shè)置cancel對象
            if (token.reason) {
              return// Cancellation has already been requested
            }
            token.reason = new Cancel(message);
            resolvePromise(token.reason);
          });
        }

        由以上代碼可知,cancel 對象是一個函數(shù),當(dāng)我們調(diào)用該函數(shù)后,會創(chuàng)建 Cancel 對象并調(diào)用 resolvePromise 方法。該方法執(zhí)行后,CancelToken 對象上 promise 屬性所指向的 promise 對象的狀態(tài)將變?yōu)?resolved。那么這樣做的目的是什么呢?這里我們從 lib/adapters/xhr.js 文件中找到了答案:

        // lib/adapters/xhr.js 
        if (config.cancelToken) {
          config.cancelToken.promise.then(function onCanceled(cancel{
            if (!request) { return; }
            request.abort(); // 取消請求
            reject(cancel);
            request = null;
          });
        }

        看完上述的內(nèi)容,可能有的小伙伴還不是很能理解 CancelToken 的工作原理,所以阿寶哥又畫了一張圖來幫助大家理解 CancelToken 的工作原理:

        五、總結(jié)

        本文介紹了在 Axios 中如何取消重復(fù)請求及 CancelToken 的工作原理。

        六、參考資源



        瀏覽 103
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            91aaa视频 | 国产免费三片 | 久久久久a v成人无码 | 一区色| 国产又爽又黄视频 | 美国毛片基地 | 青娱乐成人视频 | 大香蕉少妇| 色一综合 | 黄片入口 |