Построить поле автоподсказок поиска, отменяющее устаревшие запросы в полёте
Свяжите поле поиска, запрашивающее подсказки по мере ввода. На каждый ввод запрашивать /suggests?term=<value> и отрисовывать результаты списком под полем. Обработайте случаи: не запрашивать при пустом/из пробелов терме; при ошибке ничего не показывать; и, главное, гарантировать, что результаты более нового запроса всегда заменяют старый, даже если старый разрешится последним.
async function onInput(value) {
// ваш код здесь
}
Допишите реализацию.
Держать AbortController на уровне модуля. На каждый ввод: trim() и выйти, если пусто; прервать предыдущий контроллер, создать новый и fetch с его signal. Поскольку прерывание отменяет старый запрос в полёте, медленный ранний ответ уже не может перезаписать более новый — гонка с порядком ответов устранена. Обернуть fetch в try/catch, игнорировать AbortError, при других ошибках ничего не отрисовывать. В пару — debounce.
- ✗Игнорировать гонку запросов, из-за чего медленный старый ответ перезаписывает подсказки более нового
- ✗Давать
AbortErrorвсплыть как настоящей ошибке и очищать список, когда запрос отменили намеренно - ✗Запрашивать при пустом терме или из пробелов вместо раннего выхода
- →Как прерывание предыдущего запроса не даёт ответу не в том порядке победить?
- →Почему
AbortErrorстоит обрабатывать иначе, чем сетевую ошибку, в блоке catch?
Решение
AbortController отменяет предыдущий запрос, устраняя гонку порядка ответов.
let controller = null;
async function onInput(value) {
const term = value.trim();
if (!term) {
render([]);
return;
}
controller?.abort();
controller = new AbortController();
try {
const res = await fetch(`/suggests?term=${encodeURIComponent(term)}`, {
signal: controller.signal,
});
const suggests = await res.json();
render(suggests);
} catch (e) {
if (e.name === 'AbortError') return; // отменён намеренно — игнорируем
render([]); // настоящая ошибка — ничего не показываем
}
}
Как это работает
Без отмены возможна гонка: пользователь печатает быстро, уходит несколько запросов, и медленный ранний ответ приходит последним — затирая подсказки для более свежего терма. AbortController это лечит: на каждый ввод мы прерываем предыдущий контроллер, и его fetch отклоняется с AbortError, не доходя до render.
В catch мы отличаем AbortError (наша же отмена — просто выходим) от настоящих ошибок (ничего не показываем). Пустой терм отсекается до запроса. На практике это сочетают с debounce, чтобы не слать запрос на каждое нажатие.