axios请求封装
2022/1/30 axios
# axios的cancelToken
原文出处: https://segmentfault.com/a/1190000021290514 (opens new window)
- 通过
axios.CancelToken.source
生成取消令牌token和取消方法cancel
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 {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 通过axios.CancelToken构造函数生成取消函数
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
cancel = c;
})
});
// cancel the request
cancel();
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 封装取消请求逻辑
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
const pending = new Map()
/**
* 添加请求
* @param {Object} config
*/
const addPending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&')
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!pending.has(url)) { // 如果 pending 中不存在当前请求,则添加进去
pending.set(url, cancel)
}
})
}
/**
* 移除请求
* @param {Object} config
*/
const removePending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&')
if (pending.has(url)) { // 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pending.get(url)
cancel(url)
pending.delete(url)
}
}
/**
* 清空 pending 中的请求(在路由跳转时调用)
*/
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url)
}
pending.clear()
}
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
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
- 在 axios 拦截器中使用
axios.interceptors.request.use(config => {
removePending(options) // 在请求开始前,对之前的请求做检查取消操作
addPending(options) // 将当前请求添加到 pending 中
// other code before request
return config
}, error => {
return Promise.reject(error)
})
axios.interceptors.response.use(response => {
removePending(response) // 在请求结束后,移除本次请求
return response
}, error => {
if (axios.isCancel(error)) {
console.log('repeated request: ' + error.message)
} else {
// handle error code
}
return Promise.reject(error)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# axios超时重发
出处:https://github.com/axios/axios/issues/164#issuecomment-327837467
- axios 的超时是在 response 中处理的,所以要在 response 中添加拦截器:
const RETRY_TIMEOUTS = [1, 3, 5, 10]; // seconds
const delay = RETRY_TIMEOUTS[config.retryCount] * 1000;
axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) {
var config = err.config;
// If config does not exist or the retry option is not set, reject
if(!config || !config.retry) return Promise.reject(err);
// Set the variable for keeping track of the retry count
config.__retryCount = config.__retryCount || 0;
// Check if we've maxed out the total number of retries
if(config.__retryCount >= config.retry) {
// Reject with the error
return Promise.reject(err);
}
// Increase the retry count
config.__retryCount += 1;
// Create new promise to handle exponential backoff
var backoff = new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, config.retryDelay || delay);
});
// Return the promise in which recalls axios to retry the request
return backoff.then(function() {
return axios(config);
});
});
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
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
- 使用
axios.get('/some/endpoint', { retry: 5, retryDelay: 1000 })
.then(function(res) {
console.log('success', res.data);
})
.catch(function(err) {
console.log('failed', err);
});
1
2
3
4
5
6
7
2
3
4
5
6
7
- 重新请求1次
axios.interceptors.response.use(function(response){
// ....
}, function(error){
var originalRequest = error.config;
if(error.code == 'ECONNABORTED' && error.message.indexOf('timeout')!=-1 && !originalRequest._retry){
originalRequest._retry = true
return axios(originalRequest);
}
});
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# axios配置
// 封装axios请求
import axios from "axios"
import qs from "qs"
import router from "../../router"
// 声明一个 Map 用于存储每个请求的标识 和 取消函数
const pending = new Map()
/**
* 添加请求
* @param {Object} config
*/
const addPending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data),
].join("&")
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pending.has(url)) {
// 如果 pending 中不存在当前请求,则添加进去
pending.set(url, cancel)
}
})
}
/**
* 移除请求
* @param {Object} config
*/
const removePending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data),
].join("&")
if (pending.has(url)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
const cancel = pending.get(url)
cancel(url)
pending.delete(url)
}
}
/**
* 清空 pending 中的请求(在路由跳转时调用)
* @param {Object} config
*/
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url)
}
pending.clear()
}
const request = axios.create({
timeout: 10000,
withCredentials: false,
})
const CONFIG = {
retry: 3,
retryDelay: 1000,
}
// 添加请求拦截器
request.interceptors.request.use(
(config) => {
removePending(config)
addPending(config)
return config
},
(error) => {
return Promise.reject(error)
},
)
// 添加响应拦截器
request.interceptors.response.use(
(response) => {
// 请求返回结果,移除pending
removePending(response.config)
if (
Number(response.data.stat) === 1 ||
(response.data.result && Number(response.data.result.status) === 1)
) {
return Promise.resolve(response.data)
} else {
// 未登录
if (response.data.stat == 9) {
router.push({ name: "home" })
}
return Promise.reject(response)
}
},
(error) => {
// retry request: https://github.com/axios/axios/issues/164#issuecomment-327837467
let config = error.config
// If config does not exist or the retry option is not set, reject
if (!config || !CONFIG.retry) return Promise.reject(error)
// Set the variable for keeping track of the retry count
config.__retryCount = config.__retryCount || 0
// Check if we've maxed out the total number of retries
if (config.__retryCount >= CONFIG.retry) {
// Reject with the error
return Promise.reject(error)
}
// Increase the retry count
config.__retryCount += 1
// Create new promise to handle exponential backoff
let backoff = new Promise(function (resolve) {
setTimeout(function () {
resolve()
}, CONFIG.retryDelay)
})
// Return the promise in which recalls axios to retry the request
return backoff.then(function () {
return request(config)
})
},
)
export default request
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129