场景
有一个接口是向后端请求数据的,地址/slow,单次耗时比较久,当用户点击请求就会发送请求。
问题
当用户频繁触发或者误触发这个请求的时候,就会导致问题:
- 第二次请求可能会被第一次覆盖(因为不确定返回时间
- 增加不必要的等待时间(用户可能就不想要之前的请求
想法
如果请求可以取消,那么问题就解决了
理论
支持使用 CancelToken 取消请求,存在一下特性:
- 一个 CancelToken 可以绑定多个请求
- 当触发 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 {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
融入开发
做了一些必要的封装 Make life easier
// 慢请求处理
export class SlowRequest {
constructor() {
this.cancelToken = axios.CancelToken.source()
}
async request() {
this.cancelToken.cancel()
this.cancelToken = axios.CancelToken.source()
return await axios({
url: 'http://localhost:3000/slow',
cancelToken: this.cancelToken.token
})
}
cancel() {
this.cancelToken.cancel()
}
}
// Demo 组件
export const TestCancel = (props) => {
const [loading, setLoading] = useState(0)
const [client, _] = useState(new SlowRequest())
const onQuery = async () => {
setLoading(old => old + 1)
try{
const res = await client.request()
}catch(e) {
console.log(e)
}
setLoading(old => old - 1)
}
const onCancel = () => {
client.cancel()
}
return (
<div>
{
loading > 0 ? 'loading' : 'content'
}
<button onClick={onQuery}>query</button>
<button onClick={onCancel}>cancel</button>
</div>
)
}
不足
- 由于只是记录想法,所以封装其实并不到位,像 URL、Client 都可以再次抽离