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;
        });
    }
    
复制代码
相关文章
相关标签/搜索