В сценариях работы с удалёнными источниками данных Tom Select
опирается на функцию загрузки (load), которая отвечает за
получение и преобразование данных перед отображением вариантов. На этом
уровне формируется ключевая зона нестабильности: сетевые сбои,
некорректные ответы сервера, таймауты, ошибки формата данных.
Базовая сигнатура загрузчика:
new TomSelect("#select", {
valueField: "id",
labelField: "title",
searchField: "title",
load: function(query, callback) {
fetch(`/api/items?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => callback(data))
.catch(() => callback());
}
});
В данной схеме обработка ошибок фактически сводится к вызову
callback() без аргументов. Это приводит к пустому
результату, но не даёт информации о природе сбоя, что критично при
сложной интеграции.
Возникают при недоступности API, обрыве соединения или DNS-проблемах.
В fetch такие ситуации попадают в catch.
Характерные признаки:
Пример расширенной обработки:
load: function(query, callback) {
fetch(`/api/items?q=${encodeURIComponent(query)}`)
.then(res => {
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json();
})
.then(data => callback(data))
.catch(err => {
console.error("Load error:", err);
callback();
});
}
Даже при успешном сетевом соединении сервер может возвращать:
Tom Select не интерпретирует HTTP-статусы автоматически, поэтому контроль состояния ответа полностью ложится на пользовательскую реализацию загрузчика.
Часто встречается ситуация, когда сервер возвращает JSON, не
соответствующий ожиданиям valueField и
labelField.
Пример некорректной структуры:
[
{ "name": "Item 1" }
]
При ожидании:
[
{ "id": 1, "title": "Item 1" }
]
Результат:
Для диагностики применяется валидация:
.then(data => {
if (!Array.isArray(data)) {
throw new Error("Invalid data format");
}
callback(data);
})
Tom Select использует callback как основной механизм завершения загрузки:
callback(items);
callback();
Особенность модели заключается в отсутствии отдельного канала передачи ошибки. Это создаёт необходимость внедрения дополнительных соглашений.
.catch(err => {
logError(err);
callback();
});
Недостаток: отсутствие обратной связи для UI.
Используется кастомная структура:
.then(data => {
callback(data.items || []);
})
При этом сервер возвращает:
{
"items": [],
"error": null
}
Позволяет централизовать обработку ошибок вне компонента.
При динамическом поиске частая проблема — устаревшие запросы. Tom Select может инициировать множество загрузок при быстром вводе текста.
Решение — отмена предыдущего запроса:
let controller = null;
load: function(query, callback) {
if (controller) controller.abort();
controller = new AbortController();
fetch(`/api/items?q=${encodeURIComponent(query)}`, {
signal: controller.signal
})
.then(res => res.json())
.then(data => callback(data))
.catch(err => {
if (err.name === "AbortError") return;
callback();
});
}
Ключевой эффект:
Встроенного таймаута в стандартной конфигурации нет, поэтому
используется комбинация Promise.race.
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), timeout)
)
]);
}
Интеграция в Tom Select:
load: function(query, callback) {
fetchWithTimeout(`/api/items?q=${query}`)
.then(res => res.json())
.then(data => callback(data))
.catch(() => callback());
}
Пустой массив данных не является ошибкой, однако часто интерпретируется как сбой UX.
Различают два состояния:
Корректная реализация:
.then(data => {
if (data.length === 0) {
callback([]);
return;
}
callback(data);
})
Дополнительно возможно формирование пользовательских опций:
callback([
{ id: "", title: "Ничего не найдено" }
]);
Для сложных систем применяется обёртка над загрузчиком:
function createSafeLoader(loader) {
return function(query, callback) {
try {
loader(query, callback);
} catch (e) {
console.error(e);
callback();
}
};
}
Использование:
new TomSelect("#select", {
load: createSafeLoader(function(query, callback) {
fetch(`/api?q=${query}`)
.then(r => r.json())
.then(callback)
.catch(() => callback());
})
});
При интеграции с аналитическими системами ошибки загрузки часто фиксируются отдельно от UI-обработки.
Пример расширенного логирования:
.catch(err => {
logToService({
type: "tomselect_load_error",
message: err.message,
query
});
callback();
});
Фиксируемые параметры:
При нестабильной сети применяется стратегия повторов:
function fetchRetry(url, retries = 3) {
return fetch(url).catch(err => {
if (retries > 0) {
return fetchRetry(url, retries - 1);
}
throw err;
});
}
Интеграция:
load: function(query, callback) {
fetchRetry(`/api/items?q=${query}`)
.then(r => r.json())
.then(data => callback(data))
.catch(() => callback());
}
Tom Select предоставляет событие load, которое позволяет
отслеживать момент завершения загрузки данных.
const select = new TomSelect("#select", {
load: function(query, callback) {
callback([]);
}
});
select.on("load", () => {
console.log("Загрузка завершена");
});
На основе этого механизма строится:
При работе с внешними API часто встречаются:
Защита:
.then(async res => {
const text = await res.text();
try {
return JSON.parse(text);
} catch {
throw new Error("Invalid JSON response");
}
})
Стабильная обработка ошибок в Tom Select формируется из нескольких уровней:
Комбинация этих механизмов формирует предсказуемое поведение компонента даже при нестабильной сетевой среде и неполностью контролируемом серверном API.