В современных веб-приложениях часто возникает необходимость прерывать асинхронные запросы, особенно при работе с динамическими компонентами, автозаполнением или поиском. Vue.js предоставляет удобный инструментарий для интеграции таких механизмов, используя возможности JavaScript и сторонние библиотеки, такие как Axios или Fetch API.
Асинхронные запросы могут привести к нежелательным последствиям, если компоненты обновляются или уничтожаются до завершения запроса. Пример типичной ситуации:
export default {
data() {
return {
results: [],
query: '',
};
},
watch: {
query(newQuery) {
this.fetchData(newQuery);
}
},
methods: {
async fetchData(query) {
const response = await fetch(`https://api.example.com/search?q=${query}`);
const data = await response.json();
this.results = data.items;
}
}
};
Если пользователь быстро вводит несколько символов, предыдущие
запросы продолжают выполняться, что может привести к неправильным данным
в results или лишней нагрузке на сервер.
AbortController с Fetch APIJavaScript предоставляет встроенный объект
AbortController для отмены запросов:
export default {
data() {
return {
results: [],
query: '',
controller: null,
};
},
watch: {
query(newQuery) {
this.fetchData(newQuery);
}
},
methods: {
async fetchData(query) {
if (this.controller) {
this.controller.abort(); // Отменяем предыдущий запрос
}
this.controller = new AbortController();
try {
const response = await fetch(`https://api.example.com/search?q=${query}`, {
signal: this.controller.signal
});
const data = await response.json();
this.results = data.items;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Запрос отменен');
} else {
console.error(error);
}
}
}
}
};
Ключевые моменты:
AbortController.fetch.AbortError предотвращает появление
ошибок в консоли.Axios также поддерживает отмену запросов через
CancelToken или современный AbortController
(начиная с версии 1.2):
import axios from 'axios';
export default {
data() {
return {
results: [],
query: '',
cancelTokenSource: null,
};
},
watch: {
query(newQuery) {
this.fetchData(newQuery);
}
},
methods: {
async fetchData(query) {
if (this.cancelTokenSource) {
this.cancelTokenSource.cancel('Предыдущий запрос отменен');
}
this.cancelTokenSource = axios.CancelToken.source();
try {
const response = await axios.get(`https://api.example.com/search?q=${query}`, {
cancelToken: this.cancelTokenSource.token
});
this.results = response.data.items;
} catch (error) {
if (axios.isCancel(error)) {
console.log(error.message);
} else {
console.error(error);
}
}
}
}
};
Важно отменять запросы при уничтожении компонента, чтобы избежать ошибок:
export default {
data() {
return {
controller: null,
};
},
methods: {
async fetchData() {
this.controller = new AbortController();
await fetch('https://api.example.com/data', {
signal: this.controller.signal
});
}
},
beforeUnmount() {
if (this.controller) {
this.controller.abort();
}
}
};
Использование beforeUnmount гарантирует, что запрос не
продолжит выполняться после удаления компонента из DOM, предотвращая
утечки памяти и неожиданные изменения состояния.
watch
и дебаунсомДля улучшения производительности и снижения количества запросов часто применяют дебаунс (задержку перед вызовом метода):
import { ref, watch } from 'vue';
import debounce from 'lodash/debounce';
export default {
setup() {
const query = ref('');
const results = ref([]);
let controller = null;
const fetchData = debounce(async (value) => {
if (controller) controller.abort();
controller = new AbortController();
try {
const response = await fetch(`https://api.example.com/search?q=${value}`, {
signal: controller.signal
});
const data = await response.json();
results.value = data.items;
} catch (error) {
if (error.name !== 'AbortError') console.error(error);
}
}, 300);
watch(query, (newQuery) => {
fetchData(newQuery);
});
return { query, results };
}
};
Выводы по практике:
AbortController или
CancelToken предотвращает гонки асинхронных запросов.