# 1. 前言

到现在为止,官方axios取消请求功能我们已经实现了 99%了,那么还有 1%的功能没有完成,是什么呢?我们设想这样一个业务场景:有两个请求:请求 1 和请求 2,这两个请求都受某个条件影响,也就是说,一旦这个条件成立,这两个请求都要被取消,亦或者说,当请求 1 发出后,此时突然该条件成立了,那么立即取消了请求 1,而接下来请求 2 就不要再发了,而不是说请求 2 照样发出,只不过发出后被取消,请看如下demo所示:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios
  .get("/api/cancel", {
    cancelToken: source.token,
  })
  .catch(function(e) {
    if (axios.isCancel(e)) {
      console.log(`请求取消原因:${e.message}`);
    }
  });

setTimeout(() => {
  source.cancel("Operation canceled by the user");
}, 1000);

setTimeout(() => {
  axios
    .get("/api/cancel", {
      cancelToken: source.token,
    })
    .catch(function(e) {
      if (axios.isCancel(e)) {
        console.log(`请求取消原因:${e.message}`);
      }
    });
}, 1500);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

请求 1 和请求 2 都受某个条件影响,请求 1 发出后 1 秒该条件突然成立随即取消请求,而 1.5 秒后请求 2 照样发出,接着被取消。这样显然是造成了资源浪费,因为我们明明知道该条件一旦成立了,请求 2 发出去也会被取消,那何不如干脆不发了,这样不更好吗?

接下来,我们就来优化这个缺点。其实也很简单,就是在发送请求前加条判断即可,首先判断当前是否配置了cancelToken,其次再判断取消原因reason是否存在,若果存在说明这个 cancelToken 已经被使用过了,我们就不发送这个请求,直接抛出异常即可,并且抛异常的信息就是我们取消的原因。

# 2. 添加 throwIfRequested 方法

我们在CancelToken类里添加一个实例方法throwIfRequested,该方法用来判断取消原因reason是否存在,如果存在,则直接抛出异常,并且把取消原因作为异常信息,如下:

export default class CancelToken {
  //...
  throwIfRequested(): void {
    if (this.reason) {
      throw this.reason;
    }
  }
}
1
2
3
4
5
6
7
8

添加完之后别忘了在CancelToken类实例对象接口定义中添加该方法的接口定义:

export interface CancelToken {
  promise: Promise<Cancel>;
  reason?: Cancel;

  throwIfRequested(): void;
}
1
2
3
4
5
6

完了之后,我们最后在发送请求前做次判断,判断是否配置了cancelToken,如果配置了进而再调用throwIfRequested方法判断取消原因是否存在。

# 3. 添加请求判断逻辑

我们在src/core/dispatchRequest.ts中的dispatchRequest方法中添加判断逻辑,如下:

function dispatchRequest(config: AxiosRequestConfig): AxiosPromise {
  throwIfCancellationRequested(config);
  processConfig(config);
  return xhr(config).then((res) => {
    return transformResponseData(res);
  });
}

// 如果已经请求取消,则抛出异常。
function throwIfCancellationRequested(config: AxiosRequestConfig) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

发送请求前检查一下配置的 cancelToken 是否已经使用过了,如果已经被用过则不用法请求,直接抛异常。

这样一来,我们就把上面说的资源浪费的问题优化了,接下来,我们就编写demo来试试效果如何。

# 4. demo 编写

我们继续沿用上面前言中分析的demo,运行结果如下:

从结果中我们看到,请求 1 发出 1 秒后被取消,接着请求 2 没有被发出,并且请求 2 中捕获到的异常也是请求原因异常,异常信息就是取消原因。

至此,官方axios请求取消功能就算是 100%完成了。