Awesomplete изначально ориентирована на работу с локальным массивом данных, однако в реальных интерфейсах список автодополнения часто формируется динамически через сетевые запросы. Это приводит к появлению отдельного класса проблем: задержки ответа, сбои сети, некорректные данные и ситуации частичной загрузки.
Типовая схема работы с удалённым источником строится вокруг
обновления свойства list экземпляра Awesomplete после
завершения асинхронной операции:
const input = document.querySelector("#search");
const awesomplete = new Awesomplete(input, {
minChars: 1,
autoFirst: true,
list: []
});
async function loadSuggestions(query) {
const response = await fetch(`/api/suggest?q=${encodeURIComponent(query)}`);
const data = await response.json();
awesomplete.list = data;
}
Такая модель делает Awesomplete зависимой от внешнего состояния, а значит обработка ошибок становится критически важной частью архитектуры.
При работе с удалёнными источниками выделяются несколько устойчивых категорий ошибок:
К ним относятся:
try {
const response = await fetch(url);
} catch (err) {
// NetworkError, AbortError и другие низкоуровневые сбои
}
Даже при успешном соединении сервер может вернуть некорректный статус:
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
Awesomplete ожидает массив строк или объектов определённой структуры. Несоответствие формата приводит к некорректному отображению или полному отказу списка.
const data = await response.json();
if (!Array.isArray(data)) {
throw new Error("Invalid format: expected array");
}
Сервер может вернуть пустой массив или элементы с отсутствующими полями, что приводит к «пустому автокомплиту» или визуальным артефактам.
Стандартная модель обработки строится через try/catch с
обязательной очисткой или безопасным сбросом списка:
async function loadSuggestions(query) {
try {
const response = await fetch(`/api/suggest?q=${encodeURIComponent(query)}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
if (!Array.isArray(data)) {
throw new Error("Invalid response format");
}
awesomplete.list = data;
} catch (err) {
awesomplete.list = [];
}
}
Такая стратегия предотвращает отображение устаревших или повреждённых данных.
Одной из наиболее сложных проблем является ситуация, когда запросы выполняются быстрее или медленнее друг друга. Например, пользователь вводит текст быстро, и ответы приходят в произвольном порядке.
Это приводит к эффекту «перезаписи списка устаревшими данными».
Решение строится через идентификатор запроса:
let requestId = 0;
async function loadSuggestions(query) {
const currentId = ++requestId;
try {
const response = await fetch(`/api/suggest?q=${query}`);
const data = await response.json();
if (currentId !== requestId) {
return;
}
awesomplete.list = data;
} catch (err) {
if (currentId === requestId) {
awesomplete.list = [];
}
}
}
Такой подход гарантирует, что только последний запрос влияет на интерфейс.
Более современный способ управления устаревшими запросами —
использование AbortController.
let controller = null;
async function loadSuggestions(query) {
if (controller) {
controller.abort();
}
controller = new AbortController();
try {
const response = await fetch(`/api/suggest?q=${query}`, {
signal: controller.signal
});
const data = await response.json();
awesomplete.list = data;
} catch (err) {
if (err.name === "AbortError") {
return;
}
awesomplete.list = [];
}
}
Преимущество заключается в том, что отменённые запросы не продолжают потреблять ресурсы и не требуют дополнительной логики проверки идентификаторов.
Fetch не имеет встроенного таймаута, поэтому его моделируют через
Promise.race.
function fetchWithTimeout(url, timeout = 3000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
return fetch(url, { signal: controller.signal })
.finally(() => clearTimeout(timer));
}
Интеграция с Awesomplete:
async function loadSuggestions(query) {
try {
const response = await fetchWithTimeout(`/api/suggest?q=${query}`);
const data = await response.json();
awesomplete.list = Array.isArray(data) ? data : [];
} catch (err) {
awesomplete.list = [];
}
}
При недоступности API важно сохранять функциональность интерфейса. Используются резервные источники:
const fallbackList = ["apple", "banana", "orange", "grape"];
function setFallback() {
awesomplete.list = fallbackList;
}
const cache = new Map();
async function loadSuggestions(query) {
if (cache.has(query)) {
awesomplete.list = cache.get(query);
return;
}
try {
const response = await fetch(`/api/suggest?q=${query}`);
const data = await response.json();
cache.set(query, data);
awesomplete.list = data;
} catch (err) {
awesomplete.list = cache.get(query) || [];
}
}
Даже при успешной доставке данных важно защищаться от некорректной структуры:
function normalizeData(data) {
if (!Array.isArray(data)) return [];
return data
.filter(item => typeof item === "string" && item.length > 0)
.slice(0, 20);
}
Интеграция:
const data = await response.json();
awesomplete.list = normalizeData(data);
Для анализа проблем загрузки используется централизованное логирование:
function logError(context, error) {
console.error(`[Awesomplete:${context}]`, error);
}
Применение:
catch (err) {
logError("loadSuggestions", err);
awesomplete.list = [];
}
В продакшн-средах вместо console.error обычно
используется отправка в сервисы мониторинга.
При ошибках загрузки важно предотвращать ситуацию, когда интерфейс выглядит сломанным без обратной связи. В Awesomplete это решается косвенно — через управление списком и состоянием input.
function handleErrorState() {
awesomplete.list = [];
input.setAttribute("data-error", "true");
}
Сброс состояния:
function clearErrorState() {
input.removeAttribute("data-error");
}
На практике используется комбинация нескольких подходов:
AbortControllerlet controller = null;
const cache = new Map();
async function loadSuggestions(query) {
if (cache.has(query)) {
awesomplete.list = cache.get(query);
return;
}
if (controller) controller.abort();
controller = new AbortController();
try {
const response = await fetchWithTimeout(
`/api/suggest?q=${query}`,
3000,
controller.signal
);
const data = await response.json();
const normalized = normalizeData(data);
cache.set(query, normalized);
awesomplete.list = normalized;
} catch (err) {
awesomplete.list = cache.get(query) || [];
}
}
Такая структура минимизирует влияние сетевых и логических ошибок на поведение автодополнения и обеспечивает стабильную работу интерфейса даже при нестабильном API.