# 1. 前言

虽然我们现在已经实现了官方axios请求取消功能的两种使用方式,但是官方axios上还有个isCancel接口还未实现。该接口接收一个异常对象e作为参数,用来判断该异常是否是由取消请求导致的,如果是的话该异常对象就应该是请求取消的原因;该接口实现起来也不难,我们可以创建一个取消原因Cancel类,而把请求取消的原因作为该类的实例,这样我们在捕获异常的时候只需判断异常对象是不是Cancel类的实例,如果是的话,那么它就是请求取消原因,否则就是其他异常。OK,思路就是这样,接下来,我们就来实现它。

# 2. 定义 Cancel 接口类型

创建cancel类之前,我们先在src/types/index.ts中定义接口类型,如下:

export interface Cancel {
  message?: string;
}

export interface CancelStatic {
  new (message?: string): Cancel;
}
1
2
3
4
5
6
7

我们先定义了Cancel类的实例对象类型Cancel,它里面只有一个属性,那就是取消原因message,接着还定义了Cancel类的类类型,它里面包含了构造函数属性,构造函数接收取消原因作为参数,返回Cancel类的实例对象。

# 3. 创建 Cancel 类

接口类型定义好之后,我们就来创建Cancel类,我们在src/cancel目录下创建Cancel.ts文件,在该文件内创建Cancel类,如下:

export default class Cancel {
  message: string;
  constructor(message: string) {
    this.message = message;
  }
}
1
2
3
4
5
6

该类的实现非常简单,就是实例化一个取消原因对象,该对象的message属性就是请求的取消原因。

# 4. 创建 isCancel 方法

创建好Cancel类之后,我们还应该创建一个isCancel函数,该函数用来判断异常对象是不是取消原因对象,返回truefalse。我们在src/cancel目录下创建isCancel.ts文件,在该文件内创建isCancel函数,如下:

import Cancel from "./Cancel";

export default function isCancel(val: any): boolean {
  return val instanceof Cancel;
}
1
2
3
4
5

判断异常对象是不是取消原因对象,我们只需判断它是不是Cancel类的实例即可。

# 5. 添加 Cancel 和 isCancel 接口

创建好isCancel函数后,最后,我们将其添加到axios混合对象上,添加之前,还是要先在axios混合对象接口定义中添加isCancel属性,如下:

export interface AxiosStatic extends AxiosInstance {
  create(config?: AxiosRequestConfig): AxiosInstance;
  CancelToken: CancelTokenStatic;
  Cancel: CancelStatic;
  isCancel: (value: any) => boolean;
}
1
2
3
4
5
6

添加好接口类型以后,我们就可以在src/axios.ts中给axios混合对象添加CancelisCancel接口了,如下:

import Cancel from "./cancel/Cancel";
import isCancel from "./cancel/isCancel";
axios.Cancel = Cancel;
axios.isCancel = isCancel;
1
2
3
4

# 6. 修改之前的取消原因类型

我们现在创建取消原因Cancel类,所以我们需要将之前写的取消原因messagestring类型改成Cancel类型。需要改动如下几个地方:

src/types/index.ts

export interface CancelToken {
  promise: Promise<Cancel>;
  reason?: Cancel;
}
1
2
3
4

src/cancel/CancelToken.ts

import { Canceler, CancelExecutor, CancelTokenSource } from "../types";
import Cancel from "./Cancel";

interface ResolvePromise {
  (reason?: Cancel): void;
}

export default class CancelToken {
  promise: Promise<Cancel>;
  reason?: Cancel;
  constructor(executor: CancelExecutor) {
    let resolvePromise: ResolvePromise;

    this.promise = new Promise<Cancel>((resolve) => {
      resolvePromise = resolve;
    });

    executor((message) => {
      if (this.reason) {
        return;
      }
      this.reason = new Cancel(message);
      resolvePromise(this.reason);
    });
  }

  static source(): CancelTokenSource {
    let cancel!: Canceler;
    let token = new CancelToken((c) => {
      cancel = c;
    });
    return {
      cancel,
      token,
    };
  }
}
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
29
30
31
32
33
34
35
36
37

修改完之后,isCancel接口就算实现完毕了,接下来,我们就编写demo测试效果如何。

# 7. demo 编写

我们继续沿用上篇文章的demo,只需在src/examples/cancel/app.ts文件中捕获异常的地方添加上本篇文章实现的异常对象判断即可,如下:

axios
  .get("/api/cancel", {
    cancelToken: new CancelToken((c) => {
      cancel = c;
    }),
  })
  .catch(function(e) {
    // 新增
    if (axios.isCancel(e)) {
      console.log(`请求取消原因:${e.message}`);
    }
  });
1
2
3
4
5
6
7
8
9
10
11
12

然后运行项目,我们打开 chrome 浏览器,访问 http://localhost:8000/ (opens new window) 即可访问我们的 demo 了,我们点击 cancel,通过F12的控制台中,我们看到取消原因已经被打印出来了。