文章目录
- 一、使用 CancelToken 取消请求
- 1、创建 CancelToken.js 文件
- 2、发送请求时订阅 onCanceled 方法
- 二、使用 AbortController 取消请求
- 三、使用 json-server 测试"取消请求"功能代码
- 1、全局安装 json-server
- 2、创建 db.json 文件并监听文件
- 3、创建 index.html 测试代码
在第一篇章节 手写axios源码系列一:axios核心知识点 中,我们知道取消请求有两种配置方式:
- config.cancelToken
- config.signal
两种方式都可以取消请求,接下来我们探究一下两种取消请求的方式有何不同。
一、使用 CancelToken 取消请求
config.cancelToken 取消请求的方式是封装 CancelToken 类,使用发布/订阅模式对发送的请求进行了订阅 onCanceled 方法,然后在需要时执行 onCanceled 方法取消请求。
1、创建 CancelToken.js 文件
export default class CancelToken {
constructor(executor){
let resolvePromise;
// 给实例对象添加 promise属性
this.promise = new Promise(function promiseExecutor(resolve){
// 暴露 resolve方法,可以在外部执行 resolvePromise方法改变当前 pending状态的 promise为 fulfilled
resolvePromise = resolve;
})
// 调用执行器函数,传入一个函数
executor(function c(){
resolvePromise()
})
// 当 this.promise状态变更时执行取消请求订阅事件
this.promise.then(() => {
const i = this._listeners.length;
// 循环数组倒序执行
while(i-- > 0){
this._listeners[i](); // 执行取消请求 onCanceled方法
}
// 置空 _listeners
this._listeners = null;
})
}
// 订阅事件方法 subscribe
subscribe(listener){
if (this._listeners){
// 向 _listeners中添加监听函数
this._listeners.push(listenner)
} else {
// 创建 this._listeners容器
this._listeners = [listener]
}
}
// 静态方法 source,其实是一个工厂函数
static source(){
let cancel;
// token为 CancelToken的实例对象,带有 subscribe订阅方法
const token = new CancelToken(function executor(c){
cancel = c;
});
return { token, cancel }
}
}
2、发送请求时订阅 onCanceled 方法
什么时候订阅取消请求 onCanceled 方法呢?
发送请求应该与取消请求是相耦合的,所以应该在发送请求的时候订阅取消当前请求的方法 onCanceled 。
发送请求的代码写在 xhr.js下:
export default function xhrAdapter(config) {
// 返回一个 promise对象
return new Promise((resolve, reject) => {
// 其他代码
// ...
// 发送请求
request.send(config.data || null);
// 订阅取消请求,根据 config中是否配置了 cancelToken字段来判断是否需要取消请求
if (config.cancelToken) {
const onCanceled = () => {
request.abort(); // 中止请求
}
// 订阅取消请求 onCanceled方法
config.cancelToken.subscribe(onCanceled);
}
})
}
二、使用 AbortController 取消请求
使用 AbortController 的方式取消请求代码很简单,根据 config 对象中是否配有 signal 字段来判断,如果有则取消请求,没有则不做任何操作。
修改 xhr.js 文件代码:
export default function xhrAdapter(config) {
// 返回一个 promise对象
return new Promise((resolve, reject) => {
// 其他代码
...
// 发送请求
request.send(config.data || null);
// 订阅取消请求,根据 config中是否配置了 cancelToken字段或者 signal字段来判断是否需要取消请求
if (config.cancelToken || config.signal) {
const onCanceled = () => {
request.abort(); // 中止请求
}
// 订阅取消请求 onCanceled方法
config.cancelToken && config.cancelToken.subscribe(onCanceled);
// signal字段判断
if (config.signal){
/**
* const controller = new AbortController()
* signal是 controller.signal对象,带有三个属性
* signal:{
* aborted: false, 通信请求是否被终止(true)或未终止(false),只读属性。
* onabort: null, 监听事件。
* reason: undefined 取消请求原因。
* }
*/
// aborted 字段为 true时直接取消请求,否则添加 abort监听取消事件
config.signal.aborted ? onCanceled() : config.signal.addEventListener("abort", onCanceled);
}
}
})
}
三、使用 json-server 测试"取消请求"功能代码
json-server 是一个可以在前端本地运行,存储 json 数据格式的快速启用的服务器。可以说它就是一个 mock 数据服务,可以模仿RESTful API。
1、全局安装 json-server
npm i -g json-server
2、创建 db.json 文件并监听文件
{
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "json-server2",
"author": "typicode2"
}
]
}
监听指令:json-server --watch db.json
。
因为我们这里要测试取消请求,所以给请求添加个延迟时间,容易操作。
执行指令:json-server --watch -d 2000 db.json
,请求会在延迟2s后返回响应数据。
3、创建 index.html 测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>axios源码解析</title>
</head>
<body>
<button id="request">请求接口</button>
<button id="cancel">取消请求</button>
<script type="module">
import axios from "./axios.js";
const requestBtn = document.querySelector("#request");
const cancelBtn = document.querySelector("#cancel");
let source = null;
// let controller = null
requestBtn.addEventListener("click", function () {
// controller = new AbortController();
source = axios.CancelToken.source();
// 发送请求
axios({
method: "get",
url: "http://localhost:3000/posts",
// signal: controller.signal,
cancelToken: source.token
}).then(response => {
console.log(response);
})
});
// 取消请求
cancelBtn.addEventListener("click", function () {
// controller.abort();
source.cancel();
});
</script>
</body>
</html>