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;
相关推荐
San813_LDD1 小时前
[C语言]《Dev-C++ 报错解决手册(Day0607 精华版)》
java·前端·javascript
threelab9 小时前
Three.js 物理模拟着色器 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
武器大师729 小时前
lv_binding_js 代码解读
开发语言·javascript·ecmascript
Patrick_Wilson11 小时前
router.replace 之后紧跟 reload,页面为什么无限刷新?
javascript·react.js·浏览器
mONESY12 小时前
JavaScript 栈、队列、数组与链表核心知识点总结
javascript·面试
ZengLiangYi12 小时前
TypeScript 项目配置:tsconfig、ESM、路径别名
javascript·typescript·aigc
晓131312 小时前
【Cocos Creator 3.x】篇——第二章 入门
前端·javascript·游戏引擎
想要成为糕糕手13 小时前
前端必修课:JavaScript 数组与数据结构底层逻辑全解析
javascript·数据结构·面试
xiaofeichaichai13 小时前
React Hooks
前端·javascript·react.js
数据知道13 小时前
C++ 层拦截:修改 Blink 引擎与 V8 绑定的底层逻辑
javascript·数据采集·指纹浏览器·风控