关于axios:axios是如何取消发生请求的

7次阅读

共计 3427 个字符,预计需要花费 9 分钟才能阅读完成。

接口分析

https://github.com/axios/axio…

应用形式一
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {name: 'new name'}, {cancelToken: source.token}).catch(function (thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);
  } else {// handle error}
});
source.cancel('Operation canceled by the user.');
应用形式二
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {cancelToken: new CancelToken(function executor(c) {cancel = c;})
});
cancel();

就是增加 cancelToken 参数,值是能够一个对象,有能够勾销的操作。

源码剖析

  1. 参考应用案例,定位axios.CancelToken,找到源码

    https://github.com/axios/axio…

    isCancel.js 判断是否 cancel 对象
    module.exports = function isCancel(value) {return !!(value && value.__CANCEL__);
    };
    Cancel.js 结构 cancel 对象,用于标识 cancel 的 message 信息
    function Cancel(message) {this.message = message;}
    Cancel.prototype.toString = function toString() {return 'Cancel' + (this.message ? ':' + this.message : '');
    };
    Cancel.prototype.__CANCEL__ = true;
    module.exports = Cancel;
    CancelToken.js
    var Cancel = require('./Cancel');
    function CancelToken(executor) {
      // 执行器必须是函数
      if (typeof executor !== 'function') {throw new TypeError('executor must be a function.');
      }
      var resolvePromise;
      // 创立一个 promise,并记录 resolve 办法,以便在执行器中应用
      this.promise = new Promise(function promiseExecutor(resolve) {resolvePromise = resolve;});
      var token = this;
      // cancel 的外围办法:用 reason 来记录勾销起因,并用来判断是否曾经勾销过。并 resolve 掉下面的 promise
      executor(function cancel(message) {if (token.reason) {
       // Cancellation has already been requested
       return;
     }
    
     token.reason = new Cancel(message);
     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) {cancel = c;});
      return {
     token: token,
     cancel: cancel
      };
    };
    module.exports = CancelToken;
  2. 搜寻代码 cancelToken,定位 ib/core/dispatchRequest.js

    function throwIfCancellationRequested(config) {if (config.cancelToken) {config.cancelToken.throwIfRequested();
      }
    }
  3. 追究 throwIfCancellationRequested 调用的中央,点击函数 throwIfCancellationRequested,按 F12,查看调用的中央



    也就是说,在初始化的时候、申请胜利的时候以及申请失败的时候会调用,判断是否曾经发动申请,如果已发动,则抛出勾销起因。
    解决了用户调用 cancel 的机会,申请发动之前则拦挡申请发动,申请发动之后,无论后果如何,都不会再进入解决逻辑。
  4. 向上追究,被 promisify 的 xhr/http

    代码显示,在发动申请之前,判断是否传了 cancelToken。如果传入了,则增加 cancelToken 的 promise 回调,把申请勾销掉。留神看看 CancelToken.js,把 cancel 办法裸露进去了,用户调用 cancel 办法之后会把 promise resolve 掉。

从新梳理

  1. xhr/http,在发动申请之前,判断是否有传入 cancelToken,并对 promise 传入回调,拦挡申请的发动。所以 CancelToken.js 须要有一个 promise 告知 xhr/http 用户是否曾经执行 cancel 了。
  2. dispatchRequest.js,在初始化、申请胜利、申请失败的时候,判断是否曾经被 cancel 了,如果是就抛出谬误。所以须要 CancelToken.js 的一个变量去判断是否曾经被 cancel 过,以及一个 throwIfRequested 办法去判断。
  3. 调用 cancel 的时刻,用户可能想做一些自定义的操作。所以执行器能够由用户本人传入,或者提供一个静态方法新建一个实例,且曾经写好了执行器了。

值得学习的中央

  1. promise 咱们常常是依照规范的办法去调用的,在 promise 体内执行 resolve,reject

    new Promsie((resolve, reject) => {
      ...
      if (...) {resolve('xxx')
       } else {reject('yyy')
       }
    })

    其实能够灵便点,用变量去保留 resolve/reject,在里面执行,进步灵便度。

    let resolver;
    new Promsie((resolve, reject) => {
      ...
      resolver = resolve;
    })
    ...
    resolver('xxx')
    ...
  2. 函数的参数是函数,须要好好了解一下为什么能够在用户调用的时候,间接用 t 来示意 CancelToken.js 曾经写好的执行器

    function CancelToken(executor) {
      ...
      // executor 函数调用
      executor(function cancel(message) {if (token.reason) {return;}
         token.reason = new Cancel(message);
         resolvePromise(token.reason);
      });
      ...
    }
    CancelToken.source = function source() {
      var cancel;
      // executor 函数定义
      var token = new CancelToken(function executor(c) {cancel = c;});
      return {
     token: token,
     cancel: cancel
      };
    };

    简化一下

    var cancel;
    // 函数定义
    function executor(c) {cancel = c;}
    // 函数调用
    execotr(() => {console.log('abc'})

    也就是说,在定义函数的时候,把一个全局变量保留局部变量的信息,这样是为了援用用户传入的信息。
    因为存在变量援用,所以我认为 executor 也是闭包。

  3. 在设计一个子性能的时候,针对属性来扩大。
正文完
 0