取消请求(axios、fetch)

取消请求

作用于频繁发送请求的场景,上一个请求还没完成,现在需要发送新的请求,那就需要将前面的请求取消掉。

AbortController

AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。

fetch

示例

给 fetch 传递第二个参数,配置 signal 属性,它的值就是 AbortController 实例的 signal 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
let controller;

function fetchVideo() {
controller = new AbortController();
const signal = controller.signal;
fetch('xxx', { signal })
.then((response) => {
console.log('下载完成', response);
})
.catch((err) => {
console.error(`下载错误:${err.message}`);
});
}

我们可以创建一个按钮,用来取消请求。调用 AbortController 实例 上的 abort() 方法即可。

1
2
3
4
5
6
abortBtn.addEventListener('click', () => {
if (controller) {
controller.abort();
console.log('中止下载');
}
});

当 abort() 被调用时,这个 fetch() promise 将 reject 一个名为 AbortError 的 DOMException。

取消之后,你会发现控制台报错了,因为 fetch promise 已经 reject 了 AbortError。

如果不想看到这个错误,可以通过 try-catchfetch 包裹起来,在 catch 中打印错误即可。

axios

https://www.axios-http.cn/docs/cancellation

在低版本中的 axios v0.22.0 中,可以通过它本身提供的 cancelToken 来控制请求取消。高版本中已废弃

同理 fetch,使用 AbortController 控制请求取消。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
import axios from "axios";

let _env = import.meta.env
// 开发环境走代理,生产环境走服务端
let url = _env.MODE == 'development'?_env.VITE_APP_BASE_API:_env.VITE_HOST_URL

const service = axios.create({
baseURL:url,
timeout:5000,
})

// 存储 url 及 abort controler 对象
const ac = new Map()

// 请求拦截器
service.interceptors.request.use(
(config) => {

// 设置 map 对象, 如果存在 url, 就取消请求
if (ac.get(config.url)) {
ac.get(config.url).abort()
ac.delete(config.url)
}

const c = new AbortController()
config.signal = c.signal
ac.set(config.url, c)

return config
},
(error) => {
return Promise.reject(error)
}
)

// 响应拦截器
service.interceptors.response.use((response) => {
const { success, message, data } = response.data

// 请求成功后, 需要删除 url 对应的对象
ac.delete(config.url)

if (success) {
return data
} else {
return Promise.reject(new Error(message))
}
})

export default service

过渡 CancelToken deprecated

此 API 从 v0.22.0 开始已被弃用,不应在新项目中使用。

可以使用 CancelToken.source 工厂方法创建一个 cancel token ,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios
.get('/user/12345', { cancelToken: source.token })
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});

axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

在过渡期间,您可以使用这两种取消 API,即使是针对同一个请求:

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
const controller = new AbortController();

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

axios.get('/user/12345', {
cancelToken: source.token,
signal: controller.signal
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});

axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})

// 取消请求 (message 参数是可选的)
source.cancel('Operation canceled by the user.');
// 或
controller.abort();

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!