I have a searchbar, the results are updated on each letter, but when the user types 3 letters quickly for example, the previous requests are not cancelled so there is a ugly delay before he get his results.
I have read this https://github.com/axios/axios#cancellation and maybe I am a bit tired but I struggle very much adding it in my project. It is almost doing the opposite effect, it now takes forever. Do you have any suggetsion or maybe do you recommend a good tutorial so that I could understand this?
<input v-model="query" type="text" class="form-control" placeholder="Runner name or description"
aria-label="Runner name or description"
aria-describedby="basic-addon2">
watch: {
query: {
handler: _.debounce(function () {
this.newSearch()
}, 100)
}
},
methods: {
searchItems() {
let filters = {
q: this.query
};
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/Items/find', filters, {
cancelToken: source.token
})
.catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
}
})
.then(
response => {
if(this.Items!=null){
response.data.forEach(element => {
this.Items.push(element);
});
}
else
this.Items=response.data;
}
);
},
newSearch(){
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
source.cancel('Cancel previous request');
this.countItems();
this.searchItems();
},
showMore(){
this.startindex = this.startindex+this.nbrows;
this.searchItems();
},
countItems(){
this.countItems=10;
let filters = {
q: this.query
};
axios.post('/Items/count', filters).then(
response => {
this.countItems=response.data;
}
);
}
}
If you want to cancel a Fetch request, you need to use the AbortController API. You can use the constructor to create a new AbortController object. It has a read-only property AbortController.
If Axios has already sent the request, all cancel does is cause your Axios request to error out and ignore any response the server sends after cancellation.
The axios cancel token API is based on the withdrawn cancelable promises proposal. This API is deprecated since v0.22.0 and shouldn't be used in new projects. You can create a cancel token using the CancelToken. source factory as shown below: const CancelToken = axios.
I was able to get this to work.. The trick was to check if the cancel token existed before kicking off the API call, among other things.. I had to move the CancelToken
and cancel
variables outside of the Vue
object/component..
This example searches GitHub for repositories...
var cancel;
var CancelToken = axios.CancelToken;
new Vue({
el: "#app",
data: {
query: "",
results: "",
isLoading: false
},
methods: {
clear() {
this.isLoading = false;
this.results = "";
this.query = "";
},
handleSearch: _.debounce(function() {
this.preApiCall();
}, 300),
preApiCall() {
if (cancel != undefined) {
cancel();
console.log("cancelled");
}
this.apiCall(this.query);
},
apiCall(query) {
if (query !== "") {
this.isLoading = true;
axios({
method: "get",
url: "https://api.github.com/search/repositories",
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
params: {
q: query
}
}).then(res => {
this.results = JSON.parse(JSON.stringify(res.data.items));
this.isLoading = false;
}).catch(err => {
this.results = err.message;
throw Error(err.message);
this.isLoading = false;
});
} else {
this.clear();
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<div id="app">
<input v-model="query" @keyup.stop="handleSearch" type="text" class="form-control" placeholder="Search">
<button @click.stop="clear">Clear</button>
<div v-if="isLoading">Loading...</div>
<ul v-if="results !== ''">
<li v-for="(r, index) in results" :key="index">
{{ r.name }}
</li>
</ul>
</div>
[CodePen mirror]
Cancelled requests:
The problem is that you're creating a new canceltoken in the newSearch and canceling that instead of the original one. If you save the source on the Vue component you can check in newSearch if it exists and only cancel then. When the post Promise has completed you delete the source again so you can't cancel when it's not needed (or possible).
{
searchItems() {
let filters = {
q: this.query
};
const CancelToken = axios.CancelToken;
this.searchItemsSource = CancelToken.source();
axios.post('/Items/find', filters, {
cancelToken: this.searchItemsSource.token
})
.catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
}
})
.then(
response => {
this.searchItemsSource = undefined;
if(this.Items!=null){
response.data.forEach(element => {
this.Items.push(element);
});
}
else
this.Items=response.data;
}
);
},
newSearch(){
if (this.searchItemsSource) {
this.searchItemsSource.cancel('Cancel previous request');
}
this.countItems();
this.searchItems();
},
}
A side note about this code; I'd actually move the canceling of the previous request into the searchItems method since it never makes sense to have two running call's at the same time. It would look more like this:
{
searchItems() {
let filters = {
q: this.query
};
if (this.searchItemsSource) {
this.searchItemsSource.cancel('Cancel previous request');
}
const CancelToken = axios.CancelToken;
this.searchItemsSource = CancelToken.source();
axios.post('/Items/find', filters, {
cancelToken: this.searchItemsSource.token
})
.catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
}
})
.then(
response => {
this.searchItemsSource = undefined;
if(this.Items!=null){
response.data.forEach(element => {
this.Items.push(element);
});
}
else
this.Items=response.data;
}
);
},
newSearch(){
this.countItems();
this.searchItems();
},
}
At this point you can ask yourself if the newSearch method is needed at all, you might remove it and move the countItems call into searchItems as well. This all depends on the rest of your code and functionality though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With