问题的来源
用el-autocomplete远程获取数据时,点击输入框会触发第一次请求,然后输入搜索文字后会触发第二次请求,两次请求间隔较短,有时候会出现第二次请求比第一次请求先返回的情况,导致我们期望的第二次发送的请求返回的数据会被第一次请求返回的数据覆盖掉
解决思路
在发送第二次请求的时候如果第一次请求还未返回,则取消第一次请求,以保证后发送的请求返回的数据不会被先发送的请求覆盖。
axios官方文档取消请求说明
前端精品教程:
方法一:
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 { // 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 | 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(); |
不可行方案
注:本例采用的的axios的实例发送请求,其他情况未测试
初始方案A
请求时的代码如下:
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 | /* 接口listApi.getList方法如下 */ const CancelToken = axios.CancelToken const source = CancelToken.source() getVideoList ({ key }) { return axiosInstance.post( '/video/list' , { key }, { cancelToken: source.token }) }, cancelRequest () { // 取消请求 source.cancel() } /* 页面中获取列表的函数 */ getList (query, cb) { // 取消之前的请求 listApi.cancelRequest() // 发送请求 listApi.getVideoList({key: 'value' }).then(resp => { // handle response data }). catch (err => { if (axios.isCancel(err)) { console.log( 'Request canceled!' ) } else { this .$message.error(err.message) } }) } |
此时chrome的Network面板并未发送getVideoList请求,控制台输出Request canceled!
原因猜想如下:
执行listApi.cancelRequest()时会将listApi.getVideoList({key: 'value'})返回的Promise状态置为reject,因此在执行listApi.getVideoList({key: 'value'})时并未发送请求,而直接执行catch块中的代码,在控制台输出Request canceled!。
改进方案B
将getList方案改造如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* 页面中获取列表的函数 */ getList (query, cb) { // 发送请求 listApi.getVideoList({key: 'value' }).then(resp => { // handle response data // 取消请求 listApi.cancelRequest() }). catch (err => { if (axios.isCancel(err)) { console.log( 'Request canceled!' ) } else { this .$message.error(err.message) } }) } |
此时发送两个请求时,会在第一个请求返回后取消别一个请求,并在控制台输出Request canceled!,但当取消请求触发后,再次触发getList方法时结果同方案A。
原因猜想如下:
用方法一触发取消请求后,此后触发该请求均返回同一个已经被reject的Promise,因此此例中请求取消后再次执行getList方法时并未发送getVideoList请求,而是在控制台直接输出Request canceled!
可行方案
前端精品教程:
可行方案C
代码如下:
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 | /* 接口listApi.getList方法如下 */ const CancelToken = axios.CancelToken let cancel getVideoList ({ key }) { return axiosInstance.post( '/video/list' , { key }, { cancelToken: new CancelToken( function executor (c) { cancel = c }) }) }, cancelRequest () { // 第一次执行videoService.cancelRequest()时还未发送getVideoList请求,会报错,添加如下判断 if ( typeof cancel === 'function' ) { // 取消请求 cancel() } } /* 页面中获取列表的函数 */ getList (query, cb) { // 取消之前的请求 listApi.cancelRequest() // 发送请求 listApi.getVideoList({key: 'value' }).then(resp => { // handle response data }). catch (err => { if (axios.isCancel(err)) { console.log( 'Request canceled!' ) } else { this .$message.error(err.message) } }) } |
此时重复发送多次`getVideoList请求时,会取消之前发送的请求保证返回数据为最后一次请求返回的数据。