Поиск без гонки запросов: Fetch и AbortController
Как отменить прошлый поисковый запрос и не дать запоздавшему ответу заменить свежие результаты.
- Новый вводОтменяем предыдущий запрос
- FetchПередаём signal от AbortController
- Обновление спискаПоказываем результат актуального запроса
Пользователь ввёл слово, а поиск показывает результат для первой буквы. Запросы завершились в другом порядке: старый ответ пришёл последним и перерисовал список. Исправим это с помощью AbortController и проверки актуальности ответа. Одна отмена запроса не решает все возможные случаи.
Отмена и проверка актуальности
let active = null;
let generation = 0;
async function search(term) {
const current = ++generation;
active?.abort();
active = new AbortController();
try {
const response = await fetch('/api/search?q=' + encodeURIComponent(term), {
signal: active.signal
});
if (!response.ok) throw new Error('Поиск временно недоступен');
const data = await response.json();
if (current === generation) renderResults(data);
} catch (error) {
if (error.name !== 'AbortError' && current === generation) showError(error);
}
}renderResults и showError — функции интерфейса вашего приложения. Счётчик generation дополнительно проверяет, относится ли результат к последнему намерению пользователя. Для каждого нового запроса создаётся новый контроллер: уже отменённый сигнал нельзя превратить в свежий.
HTTP-ошибка — тоже ответ
Fetch не обязан отклонять Promise при статусе 404 или 500. Поэтому response.ok проверяется отдельно от обработки сетевого исключения. Ошибка разбора JSON также должна привести к понятному состоянию интерфейса, а не оставить индикатор загрузки навсегда.
Отмена на клиенте не гарантирует прекращение всех вычислений на сервере. Дорогой поиск дополнительно ограничивается на backend, а для частого ввода можно добавить небольшую задержку перед отправкой. Эти меры решают разные задачи: задержка уменьшает частоту, отмена и номер поколения защищают актуальность выдачи.
Как проверить гонку
Задержите ответ на короткий запрос, затем быстро введите длинное слово. Запоздавший ответ не должен заменить свежую выдачу. Ещё три полезных случая: очистка поля, отключённая сеть и уход со страницы до завершения запроса.
