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;
相关推荐
PYB38 小时前
【Web·JS·基础】函数的使用和展运算符...objs
前端·javascript
默_笙9 小时前
🚥 给 RAG 装一台心电监护仪:LangSmith 全链路观测(上)
前端·javascript
变与不变80611 小时前
js同步和异步难点重点详解
开发语言·javascript·ecmascript
Dovis(誓平步青云)11 小时前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
Dovis(誓平步青云)12 小时前
几个方案来回选不定?做一个随时切换的候选推荐页
android·java·服务器·开发语言·javascript·数据库·智能化
可乐鸡翅yeah_14 小时前
fmp4 碎片 MP4 格式 HLS 流,和传统 TS 分片有什么不一样
开发语言·javascript·ios·音视频·safari·m3u8·m3u8在线播放
ss27314 小时前
AI全栈实战 | 2.3-01 组件库实战:Element Plus 怎么搭出专业界面,过度封装为什么成维护负担
前端·javascript·elementui·vue
福兮说15 小时前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
小的时候可菜了15 小时前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
可乐鸡翅yeah_15 小时前
hls.js 配置 loadStartPosition,点播起始播放位置踩坑
开发语言·javascript·网络协议·ecmascript·m3u8在线