Axios取消請求CancelToken

項目中遇到的場景,change 事件觸發模糊匹配時,短期內發送多個 ajax 請求,最後一次返回的數據可能不是最後一次觸發 change 時的請求,致使獲取數據不匹配ios

解決方案

使用的 Axios 作數據請求,使用 cancel token 取消請求

官方案例 github.com/axios/axiosgit

// using the CancelToken.source factory 
    const CancelToken = axios.CancelToken 
    const source = CancelToken.source()
    
    // get
    axios.get('/user/1', {
        cancelToken: source.token
    }).catch(function (thrown) {
        if(axios.isCancel(thrown) {
            console.log('Request canceled', thrown.message)
        }) else {
            // handle error
        }
    })
    
    // post
    axios.post('/user/1', {
        name: ''
    }, {
        cancelToken: source.token
    })
    
    // cancel request 參數可選
    source.cancel('取消上次請求')

複製代碼
// use executor function
    const CancelToken = axios.CancelToken
    let cancel
    
    // get
    axios.get('/user/1', {
        cancelToken: new CancelToken(function executor(c) {
            // executor 函數接收一個 cancel 函數做爲參數
            cancel = c
        })
    })
    
    // post
    axios.post('/user/1', {
        name: ''
    }, {
        cancelToken: new CancelToken(function executor(c) {
            cancel = c
        })
    })
    
    // cancel request
    cancel()

複製代碼

個人 Vue 項目實例github

import axios from 'axios'
    let cancel
    let CancelToken
    
    mounted() {
        CancelToken = axios.CancelToken
    }
    
    // 屢次觸發fetchList請求 取消上次請求,觸發最新請求
    async fetchList() {
        if(cancel) {
            cancel()
        }
        await axios.post('/user/list', {
            query: ''
        }, {
            cancelToken: new CancelToken(function executor(c) {
                cancel = c
            })
        })
    }

複製代碼

原生XHR

原生的 XHR 對象是調用 abort()方法取消 ajax 請求ajax

let xhr
    if (window.XMLHttpRequest) {
      xhr = new XMLHttpRequest()
    } else {
      xhr = new ActiveXObject('Microsoft.XMLHTTP')
    }
    xhr= new XMLHttpRequest()
    xhr.open('GET', 'https://api')
    xhr.send()
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        // success
      } else {
        // error
      }
    }
    // 取消ajax請求 readyState = 0
    xhr.abort()

複製代碼

Axios 源碼輕解析 CancelToken

axios/lib/cancel/CancelToken.jsaxios

 'use strict';

    var Cancel = require('./Cancel');
    
    function CancelToken(executor) {
        if (typeof executor !== 'function') {
            throw new TypeError('executor must be a function.');
        }
        /** * 定義 resolvePromise * 新建promise實例 * 將 promise的resolve方法賦值給 resolvePromise 目的是爲了在promise對象外使用resolvePromise方法來改變對象狀態 */
        var resolvePromise;
        this.promise = new Promise(function promiseExecutor(resolve) {
            resolvePromise = resolve;
        });
        /** * 將CancelToken實例賦值給token * 給executor傳入cancel方法,cancel可調用resolvePromise方法 */
        var token = this;
        executor(function cancel(message) {
            if (token.reason) {
                // 取消已響應 返回
                return;
            }
            token.reason = new Cancel(message);
            // 這裏執行的就是promise的resolve方法,改變狀態
            resolvePromise(token.reason);
      });
    }
    
    CancelToken.prototype.throwIfRequested = function throwIfRequested() {
        if (this.reason) {
            throw this.reason;
        }
    };
    
    
    CancelToken.source = function source() {
        var cancel;
        var token = new CancelToken(function executor(c) {
            // c 就是CancelToken中給executor傳入的cancel方法
            cancel = c;
        });
        return {
            token: token,
            cancel: cancel
        };
    };
    
    module.exports = CancelToken;

複製代碼

執行 promise.resolve() 後如何取消 ajax 請求api

  1. CancelToken 添加到 axiosCancelToken屬性上
// axios/lib/axios.js
    
    axios.Cancel = require('./cancel/Cancel');
    axios.CancelToken = require('./cancel/CancelToken');
    axios.isCancel = require('./cancel/isCancel');
複製代碼
  1. CancelTokenresolve 的方法觸發 promise.then 方法
// axios/lib/adapters/xhr.js
    
    // 建立XHR對象
    var request = new XMLHttpRequest()
    // 模擬當前ajax請求
    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true)
    
    if (config.cancelToken) {
        config.cancelToken.promise.then(function onCanceled(cancel) {
            if (!request) {
                return;
            }
            // 取消ajax請求
            request.abort();
            reject(cancel);
            // Clean up request
            request = null;
        });
    }
    
複製代碼
相關文章
相關標籤/搜索