axios取消请求逻辑

axios是一款基于xml和 http请求对象 封装的请求库, 本文梳理其请求中断逻辑

1. 中断函数的使用:

javascript 复制代码
// 引入axios
const axios = require('axios');

// 创建取消令牌的源
const cancelTokenSource = axios.CancelToken.source();

// 发起一个请求
axios.get('https://example.com/api/data', {
  cancelToken: cancelTokenSource.token
}).then(response => {
  console.log('请求成功:', response.data);
}).catch(error => {
  if (axios.isCancel(error)) {
    console.log('请求被取消:', error.message);
  } else {
    // 处理其他错误
    console.error('请求出错:', error);
  }
});

// 如果需要取消请求
cancelTokenSource.cancel('取消请求,例如用户导航到其他页面');

2. cancelToken的source作用如下

javascript 复制代码
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

module.exports = CancelToken;

new 出cancelToken的实例,定义executor函数,利用executor的形参获取到c, c为取消函数

3. cancelToken代码如下

javascript 复制代码
// 文件路径 Axios/lib/cancel/CancelToken.js

// ...

/**
 * A `CancelToken` is an object that can be used to request cancellation of an operation.
 *
 * @class
 * @param {Function} executor The executor function.
 */
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;

  // eslint-disable-next-line func-names
  this.promise.then(function(cancel) {
    if (!token._listeners) return;

    var i;
    var l = token._listeners.length;

    for (i = 0; i < l; i++) {
      token._listeners[i](cancel);
    }
    token._listeners = null;
  });

  // eslint-disable-next-line func-names
  this.promise.then = function(onfulfilled) { // ...
  };

  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

// ...

cancelToken类会立即执行 executor函数,将取消方法作为实参传递, source中cancel接到的形参便是executor中的cancel实参, 外部执行cancel执行的是resolvePromise方法

javascript 复制代码
 executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });

resolvePromise 用于存放cancelToken类中promise的resolve, promise用于控制中断相关操作的执行。 当resolvePromise执行, promsie被放行,则会执行cancelToken中的listeners

javascript 复制代码
  var resolvePromise;

  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });
javascript 复制代码
 this.promise.then(function(cancel) {
    if (!token._listeners) return;

    var i;
    var l = token._listeners.length;

    for (i = 0; i < l; i++) {
      token._listeners[i](cancel);
    }
    token._listeners = null;
  });

listenters是注册的中断函数,注册方法如下:

javascript 复制代码
// 文件路径 Axios/lib/cancel/CancelToken.js

// ...

/**
 * Subscribe to the cancel signal
 */

CancelToken.prototype.subscribe = function subscribe(listener) {
  // reason 值不为 undefined 说明该请求已取消,可直接调用 listener
  if (this.reason) {
    listener(this.reason);
    return;
  }

  if (this._listeners) {
    this._listeners.push(listener);
  } else {
    this._listeners = [listener];
  }
};

// ...

4. 核心请求方法

javascript 复制代码
// Axios/lib/adapters/xhr.js

// ...
    if (config.cancelToken || config.signal) {
      // Handle cancellation
      // eslint-disable-next-line func-names
      onCanceled = function(cancel) {
        if (!request) {
          return;
        }
        reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
        request.abort();
        request = null;
      };

      config.cancelToken && config.cancelToken.subscribe(onCanceled);
      if (config.signal) {
        config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
      }
    }
    
// ...

在走请求流程时,如果当前请求配置有cnacelToken, 定义onCanceled 中断函数,并通过subscribe注册到cancelToken类中, 等待resolvePromise 放行promise, 从而执行中断逻辑。

总结

javascript 复制代码
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

回顾一下source方法,

javascript 复制代码
CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

source返回了两个属性,

cancel是中断方法, 用于用户手动中断请求,

javascript 复制代码
source.cancel('取消请求,例如用户导航到其他页面');

token为cancelToken实例, 显式的声明在axios请求上

javascript 复制代码
axios.get('https://example.com/api/data', {
  cancelToken: cancelTokenSource.token
}).then

当axios获取到cancelToken时,会定义中断请求的具体逻辑,通过cancelToken的 subscribe 进行订阅

cancelToken类本身存在一个promise, resolve方法被存放在变量中, 当手动触发cancel方法时, resolve会放行, promsie.then会执行先前订阅的中断逻辑。

而中断逻辑则是给当前请求的promsie一个reject, 并且手动abort网络请求

javascript 复制代码
 reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
 request.abort();
 request = null;
相关推荐
正在学习前端的---小方同学1 分钟前
vue-easy-tree树状结构
前端·javascript·vue.js
键盘不能没有CV键4 小时前
【图片处理】✈️HTML转图片字体异常处理
前端·javascript·html
你的人类朋友6 小时前
【Node】认识multer库
前端·javascript·后端
可触的未来,发芽的智生7 小时前
新奇特:黑猫警长的纳米世界,忆阻器与神经网络的智慧
javascript·人工智能·python·神经网络·架构
前端开发爱好者8 小时前
尤雨溪官宣:"新玩具" 比 Prettier 快 45 倍!
前端·javascript·vue.js
欧阳呀8 小时前
Vue+element ui导入组件封装——超级优雅版
前端·javascript·vue.js·elementui
天***88969 小时前
js封装一个双精度算法实现
开发语言·前端·javascript
胡斌附体9 小时前
使用Electron创建helloworld程序
前端·javascript·electron·nodejs·pc
toobeloong9 小时前
Electron 从低版本升级到高版本 - webview通信的改造
前端·javascript·electron