JavaScript: Abort your fetch
or how to implement an autocomplete with VueJS + VuetifyJS

Today I want to share me recent learnings on implementing an autocomplete with VuetifyJS using the fetch API.
Therefore I added this tag to my page
<v-combobox
v-model.trim="selectedPlace"
:items="foundPlaces"
:loading="isPlacesLoading"
:search-input.sync="searchPlace"
hide-no-data
hide-selected
label="Ort"
clearable
:rules="nameRules"
/>
that should render like this

The code handling the search using the AbortController:
watch: {
async searchPlace (name) {
if (!name) return
if (this.isPlacesLoading) {
this.searchFetchController.abort()
}
this.searchFetchController = new AbortController()
this.isPlacesLoading = true
const url = `http://api.geonames.org/searchJSON?q=${name}&username=demo`
try {
const response = await fetch(url, {
method: 'GET', signal: this.searchFetchController.signal
})
const responseAsJson = await response.json()
this.foundPlaces = responseAsJson.geonames
.map(({ geonameId, name, countryName, ...rest }) => ({ value: geonameId, text: `${name} (${countryName})`, ...rest }))
this.isPlacesLoading = false
} catch (exception) {
if (exception instanceof DOMException) { // ignore aborts
} else {
this.isPlacesLoading = false
console.log(exception)
}
}
},
So what am I doing here?
The AbortController, as the name suggests, can be used to abort something, here a fetch request. To use it you have to create an instance like above:
this.searchFetchController = new AbortController()
Now this controller can be used to abort multiple requests at once by adding its signal instance to each request — here only one request.
const response = await fetch(url, {
method: 'GET', signal: this.searchFetchController.signal
})
In case of aborting a request you simply have to call the abort method of the controller, signaling all configured requests, that they should abort.
this.searchFetchController.abort()
Doing this throws a DOMException that should be catched.
} catch (exception) {
if (exception instanceof DOMException) { // ignore aborts
} else {
this.isPlacesLoading = false
console.log(exception)
}
}
The interesting thing here is that this catch handles the previous made request and is thrown when we return from the method call. So it should not update isPlacesLoading otherwise it would overwrite it from the current call.
The other thing to mention here is that you always need a fresh Controller, when the actual has receives an abort.