0. 系列文章

1.使用Typescript重构axios(一)——写在最前面

2.使用Typescript重构axios(二)——项目起手,跑通流程

3.使用Typescript重构axios(三)——实现基础功能:处理get请求url参数

4.使用Typescript重构axios(四)——实现基础功能:处理post请求参数

5.使用Typescript重构axios(五)——实现基础功能:处理请求的header

6.使用Typescript重构axios(六)——实现基础功能:获取响应数据

7.使用Typescript重构axios(七)——实现基础功能:处理响应header

8.使用Typescript重构axios(八)——实现基础功能:处理响应data

9.使用Typescript重构axios(九)——异常处理:基础版

10.使用Typescript重构axios(十)——异常处理:增强版

11.使用Typescript重构axios(十一)——接口扩展

12.使用Typescript重构axios(十二)——增加参数

13.使用Typescript重构axios(十三)——让响应数据支持泛型

14.使用Typescript重构axios(十四)——实现拦截器

15.使用Typescript重构axios(十五)——默认配置

16.使用Typescript重构axios(十六)——请求和响应数据配置化

17.使用Typescript重构axios(十七)——增加axios.create

18.使用Typescript重构axios(十八)——请求取消功能:总体思路

19.使用Typescript重构axios(十九)——请求取消功能:实现第二种使用方式

20.使用Typescript重构axios(二十)——请求取消功能:实现第一种使用方式

21.使用Typescript重构axios(二十一)——请求取消功能:添加axios.isCancel接口

22.使用Typescript重构axios(二十二)——请求取消功能:收尾

23.使用Typescript重构axios(二十三)——添加withCredentials属性

24.使用Typescript重构axios(二十四)——防御XSRF攻击

25.使用Typescript重构axios(二十五)——文件上传下载进度监控

26.使用Typescript重构axios(二十六)——添加HTTP授权auth属性

27.使用Typescript重构axios(二十七)——添加请求状态码合法性校验

28.使用Typescript重构axios(二十八)——自定义序列化请求参数

29.使用Typescript重构axios(二十九)——添加baseURL

30.使用Typescript重构axios(三十)——添加axios.getUri方法

31.使用Typescript重构axios(三十一)——添加axios.all和axios.spread方法

32.使用Typescript重构axios(三十二)——写在最后面(总结)

项目源码请猛戳这里!!!

1. 前言

通过上篇文章分析,我们知道:

  • 请求配置对象中有一个请求取消令牌cancelToken属性,该属性对应一个取消请求的触发函数;
  • 当在请求外部调用了该触发函数,表示此时需要取消请求了,那么我们此时调用XMLHttpRequest对象上的abort()方法将请求取消即可。
  • axios混合对象上又多了一个静态接口CancelToken;
  • CancelToken接口是一个类;
  • CancelToken类的构造函数接收一个函数作为参数;
  • 并且这个参数函数也接收一个取消函数作为参数;

接下来,我们就基于以上这些信息来实现取消请求的第二种使用方式。

2. 定义接口类型

在创建CancelToken类之前,我们先在src/types/index.ts中定义一下相关的接口类型。

  • CancelToken类的实例对象接口类型

    CancelToken类的实例对象包含两个参数:一个必选参数是promise ,类型是Promise<string>,因为它要接收字符串类型的取消原因作为参数;另一个是可选参数取消原因reason,类型是string

    export interface CancelToken {
    promise:Promise<string>
    reason?:string
    }
  • CancelToken类的构造函数的参数类型

    因为CancelToken类的构造函数接收一个函数作为参数,因此,我们也需要定义一个该参数函数的类型。该参数函数又接收一个取消函数作为参数,它的类型是Canceler

    export interface CancelExecutor {
    (cancel: Canceler): void
    }
  • 取消函数类型Canceler

    取消函数接收错误原因message作为参数。

    export interface Canceler {
    (message?: string): void
    }
  • 修改AxiosRequestConfig

    既然请求配置对象上多了一个cancelToken属性,那当然需要再请求配置对象的接口类型定义上添加该属性。

    export interface AxiosRequestConfig {
    // ...
    cancelToken?:CancelToken
    // ...
    }

3. 创建CancelToken类

接口定义好之后,我们就可以来创建CancelToken类了,我们在src下新建cancel目录,并在该目录下创建CancelToken.ts,在该文件内创建CancelToken类,如下:

import { CancelExecutor } from "../types";

interface ResolvePromise {
(reason?: string): void;
} export default class CancelToken {
promise: Promise<string>;
reason?: string;
constructor(executor: CancelExecutor) {
let resolvePromise: ResolvePromise; this.promise = new Promise<string>(resolve => {
resolvePromise = resolve;
}); executor(message => {
if (this.reason) {
return;
}
this.reason = message;
resolvePromise(this.reason);
});
}
}

代码说明:

  1. CancelToken 构造函数内部,首先实例化了一个 pending 状态的 Promise 对象,然后用一个 resolvePromise 变量指向 resolve 函数。

  2. 接着执行 executor 函数,该函数接收的参数是: cancel 函数,即:

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

cancel 函数就是将来的请求取消触发函数,当外部调用了 cancel 函数,在 cancel 函数内部,会调用 resolvePromisePromise 对象从 pending 状态变为 resolved 状态。

4. 实现请求取消逻辑

上篇文章说过,实现请求取消实际上是调用了XMLHttpRequest对象上的abort()方法,在本项目中,能接触到XMLHttpRequest对象的就只有在src/core/xhr.ts中的请求核心函数xhr()内了,所以我们需要在该函数中实现请求取消的逻辑,并且请求取消指的是发出请求后取消,所以我们需要把这段逻辑实现在xhr()函数内的request.send()之后,如下:

const {
// ...
cancelToken
} = config; // ... // 3.发送请求
request.send(data); if (cancelToken){
cancelToken.promise.then(reason => {
request.abort();
reject(reason)
})
}
// ...

代码说明:

  • 首先判断用户是否配置的cancelToken,如果没有配置,表示没有取消请求这项需求;
  • 如果配置了cancelToken,并且当外部调用了请求取消触发函数,此时cancelToken.promise会变成resolved 状态,然后就会执行then函数,在then函数内部调用XMLHttpRequest对象上的abort()方法取消请求。

OK,请求取消的逻辑就已经实现完了,接下来我们为axios混合对象添加CancelToken静态接口。

5. 添加CancelToken接口

axios混合对象添加接口非常简单,我们已经添加过好几回了,我们仿照上次添加create接口那样,首先在src/types/index.ts中的axios混合对象接口定义中添加CancelToken,如下:

export interface CancelTokenStatic {
new(executor: CancelExecutor): CancelToken }
export interface AxiosStatic extends AxiosInstance {
create(config?: AxiosRequestConfig): AxiosInstance;
CancelToken:CancelTokenStatic
}

由于CancelToken是一个类,我们还要为这个类定义一个类类型接口CancelTokenStatic,该接口里面有一个构造函数接口。

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

import CancelToken from "./cancel/CancelToken";
axios.CancelToken = CancelToken

OK,到此为止,官方axios取消请求的第二种使用方式就实现好了,在做demo之前,我们先来梳理一下整个流程。

6. 使用流程梳理

我们使用如下方式取消请求时,代码内部的流程都是怎样的,我们现在就来梳理下。

const CancelToken = axios.CancelToken;
let cancel; axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
// executor函数接收一个取消函数作为参数
cancel = c;
})
}); cancel('Operation canceled by the user.');

流程梳理:

  1. 用户为请求配置了cancelToken属性,该属性的属性值是CancelToken类的实例。

  2. 实例化CancelToken类时,会执行类的构造函数,我们为构造函数传入了一个executor函数,如下:

    function executor(c) {
    // An executor function receives a cancel function as a parameter
    // executor函数接收一个取消函数作为参数
    cancel = c;
    }

    该函数接收一个参数,并把这个参数赋给了变量cancel

  3. 在构造函数内部,首先实例化了一个 pending 状态的 Promise 对象,然后用一个 resolvePromise 变量指向 resolve 函数。

  4. 接着执行了传入的 executor 函数,在执行 executor 函数的时候为其传入了一个参数,该参数如下:

    message => {
    if (this.reason) {
    return;
    }
    this.reason = message;
    resolvePromise(this.reason);
    }
  5. 执行了executor 函数,该函数会把这个参数赋给变量cancel,即变量cancel为:

    let cancel = message => {
    if (this.reason) {
    return;
    }
    this.reason = message;
    resolvePromise(this.reason);
    }
  6. 变量cancel 就是将来的请求取消触发函数,当外部取消请求时就会调用 cancel 函数,在 cancel 函数内部,会调用 resolvePromisePromise 对象从 pending 状态变为 resolved 状态,也就是说把CancelToken类中的this.promise变成了 resolved 状态。

  7. 而请求配置对象中的cancelToken属性是CancelToken类的实例对象,那么它自然能够访问到类里面的promise属性,当该属性的状态变成resolved 时,表明有人在外面调用了cancel 函数,此时它就通过

    cancelToken.promise.then(reason => {
    request.abort();
    reject(reason)
    })

    来取消请求。

  8. 如果没有人调用cancel 函数,那么cancelToken.promise的状态就一直是pendding,就不能调用then方法,就不能取消请求。

以上就是代码的整个运行流程。接下来我们就来编写demo来测试以上流程是否正确。

7. demo编写

examples 目录下创建 cancel目录,在 cancel目录下创建 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>cancel demo</title>
</head>
<body>
<script src="/__build__/cancel.js"></script>
</body>
</html>

接着再创建 app.ts 作为入口文件:

import axios from "../../src/axios";
import { Canceler } from "../../src/types"; const CancelToken = axios.CancelToken;
let cancel: Canceler; axios.get("/api/cancel", {
cancelToken: new CancelToken(c => {
cancel = c;
})
})
.catch(function(e) {
console.log(e);
}); setTimeout(() => {
cancel("Operation canceled by the user");
}, 1000);

接着在 server/server.js 添加新的接口路由:

// 取消请求
router.get("/api/cancel", function(req, res) {
setTimeout(() => {
res.json({
msg: `hello world`
});
}, 3000);
});

我们设置响应在发出收到请求3秒后再响应,而在请求中,我们配置的是请求发出后1秒就取消请求,从而验证是否能够取消请求。

最后在根目录下的index.html中加上启动该demo的入口:

<li><a href="examples/cancel">cancel</a></li>

OK,我们在命令行中执行:

# 同时开启客户端和服务端
npm run server | npm start

接着我们打开 chrome 浏览器,访问 http://localhost:8000/ 即可访问我们的 demo 了,我们点击 cancel,通过F12network 部分我们可以看到:请求发出一秒后请求状态变成canceled,表明请求已经被成功取消了。

然后我们将demo中的取消请求触发函数注释,

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

再发送请求,我们看到3秒后请求又可以正常得到响应了。

OK,取消请求的第二种使用方式就已经实现完毕了。

(完)

使用Typescript重构axios(十九)——请求取消功能:实现第二种使用方式的更多相关文章

  1. 使用Typescript重构axios(十八)——请求取消功能:总体思路

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  2. 使用Typescript重构axios(二十一)——请求取消功能:添加axios.isCancel接口

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  3. 使用Typescript重构axios(十六)——请求和响应数据配置化

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  4. 使用Typescript重构axios(二十二)——请求取消功能:收尾

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  5. 使用Typescript重构axios(十)——异常处理:增强版

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  6. 使用Typescript重构axios(十二)——增加参数

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  7. 使用Typescript重构axios(十四)——实现拦截器

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  8. 使用Typescript重构axios(十五)——默认配置

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  9. 使用Typescript重构axios(二十)——请求取消功能:实现第一种使用方式

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

随机推荐

  1. Android系统介绍与框架

    一.Andriod是什么? Android系统是Google开发的一款开源移动OS,Android中文名被国内用户俗称“安卓”.Android操作系统基于Linux内核设计,使用了Google公司自己 ...

  2. Nebula 架构剖析系列(零)图数据库的整体架构设计

    Nebula Graph 是一个高性能的分布式开源图数据库,本文为大家介绍 Nebula Graph 的整体架构. 一个完整的 Nebula 部署集群包含三个服务,即  Query Service,S ...

  3. python编程基础之十二

    列表:一种有序的集合,可以同时存储多个数据,列表元素可修改,属于可变序列 创建列表: 列表名 = [列表选项一,列表选项二,列表选项三,......] list1 = [] list2 = [10,2 ...

  4. .net mvc web api Autofac依赖注入框架-戈多编程

    今天自己搭了一套基于三层的依赖注入mvc web api 的依赖注入框架,在此总结下相关配置 1.设置应用程序的.net Framework版本为 4.5 2.通过Nuget 安装autofac包 I ...

  5. Color Length UVALive - 5841

    题文:见网页:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=sho ...

  6. C# 动态(不定)类型和不定参数数量,使用param写入CSV文档的最简单方法,提供excel(或记事本)阅读支持格式

    在开发一个项目,使用C#写入CSV文件时,虽并未遇到太多阻碍,但是很多小伙伴估计和我有过同样的想法.简单的写入CSV,固定参数数量就好了很简单写完.但是如果遇到你得到的数据参数数量和参数类型未知或者动 ...

  7. 重大升级!SEER见证人,您的节点需要在10月28日前更新

    SEER的区块链底层目前还处于不断完善中.一些新的完善更新会为区块链的基础设施--节点软件添加新的功能.理事会将会就是否接受新的节点版本进行共识投票,如果提案投票通过,将要求所有见证人在指定时间前将节 ...

  8. EF通过导航属性取出从表的集合后,无法删除子表

    主从表是配了级联删除的,如果通过导航属性去除从表明细删除时将报错The relationship could not be changed because one or more of the for ...

  9. 构造函数语义学——Copy Constructor 篇

    构造函数语义学--Copy Constructor 篇 本文主要介绍<深度探索 C++对象模型>之<构造函数语义学>中的 Copy Constructor 构造函数的调用时机 ...

  10. vue-cli 中stylus写样式莫名报错?

    报错一: expected "indent", got "eos" 错误截图如下: 在确认stylus安装无误后,我们应该看看是否stylus代码不符合规范. ...