События загрузки

События загрузки в Tom Select формируют основу работы с асинхронными источниками данных, где список опций не задан статически, а подгружается по мере ввода пользователя. Модель загрузки построена вокруг функции load(query, callback), управляемого жизненного цикла запросов и набора событий, отражающих состояние получения данных, успешного ответа или ошибки.

Механизм загрузки в Tom Select основан на делегировании запроса внешнему источнику данных. В отличие от статического режима, где список options заранее известен, асинхронный режим предполагает динамическое формирование данных:

  • пользователь вводит запрос;
  • Tom Select инициирует загрузку;
  • выполняется запрос к API;
  • данные преобразуются в формат опций;
  • список обновляется.

Ключевой точкой интеграции выступает функция:

load: function(query, callback) {
    fetch(`/api/search?q=${encodeURIComponent(query)}`)
        .then(res => res.json())
        .then(data => callback(data))
        .catch(() => callback());
}

callback является обязательным механизмом завершения загрузки: его вызов сигнализирует компоненту, что данные получены или запрос завершился ошибкой.


Событие загрузки данных (load)

Событие load возникает после успешного получения данных и их передачи через callback. Оно используется для постобработки загруженного набора опций.

Сигнатура обработчика:

tomSelect.on('load', function(options, query) {
    console.log('Загружено элементов:', options.length);
});

Поведение события:

  • срабатывает после успешного завершения load();
  • получает массив опций и исходный поисковый запрос;
  • вызывается после нормализации данных внутри Tom Select;
  • не вызывается при ошибке запроса.

Типичный сценарий использования — аналитика загрузки или дополнительная модификация данных:

tomSelect.on('load', (options, query) => {
    options.forEach(opt => {
        opt.label = opt.label.toUpperCase();
    });
});

Важно учитывать, что изменение данных на этом этапе не всегда влияет на уже отрендеренный список, если рендеринг завершён.


Событие ошибки загрузки (load_error)

При сбое сетевого запроса или некорректном ответе API вызывается событие load_error. Оно является ключевым механизмом обработки нестабильных источников данных.

tomSelect.on('load_error', function(query, error) {
    console.error('Ошибка загрузки:', query, error);
});

Характеристики:

  • вызывается при исключении в load;
  • срабатывает при вызове callback() без данных в ошибочном сценарии;
  • позволяет реализовать fallback-логику;
  • не блокирует работу компонента.

Типичные причины:

  • HTTP 500/404;
  • таймаут запроса;
  • неверный формат JSON;
  • отмена запроса при новом вводе.

Пример обработки с повторной попыткой:

tomSelect.on('load_error', (query) => {
    setTimeout(() => {
        tomSelect.load(query);
    }, 1000);
});

Состояние загрузки (loading state)

Хотя отдельного публичного события loading в классическом API нет, состояние загрузки отражается через внутренние классы и поведение компонента. При активном запросе интерфейс переходит в состояние ожидания.

Основные признаки:

  • блокировка повторных запросов (в зависимости от throttle);
  • отображение индикатора загрузки;
  • временная приостановка обновления списка.

Конфигурационные параметры, влияющие на поведение:

  • loadThrottle — задержка между запросами;
  • shouldLoad(query) — контроль необходимости запроса;
  • preload — предварительная загрузка данных.

Пример контроля загрузки:

shouldLoad: function(query) {
    return query.length > 2;
}

Инициация загрузки и жизненный цикл запроса

Загрузка данных проходит несколько стадий:

  1. Ввод пользователя;
  2. Проверка shouldLoad;
  3. Применение throttle;
  4. Вызов load(query, callback);
  5. Получение ответа;
  6. Обработка данных;
  7. Событие load или load_error;
  8. Обновление интерфейса.

Особое значение имеет шаг throttling. Он предотвращает чрезмерное количество запросов при быстром вводе:

loadThrottle: 300

Это значение задаёт минимальный интервал между запросами в миллисекундах.


Отмена предыдущих запросов и гонки состояний

При асинхронной загрузке часто возникает проблема race condition: пользователь вводит новый запрос до завершения предыдущего. Tom Select решает это через механизм игнорирования устаревших ответов.

Сценарий:

  • запрос A отправлен;
  • запрос B отправлен до завершения A;
  • ответ B приходит раньше;
  • ответ A приходит позже и игнорируется.

Это предотвращает «перетирание» актуальных данных.

Пример API с поддержкой abort:

let controller;

load: function(query, callback) {
    if (controller) controller.abort();
    controller = new AbortController();

    fetch(`/api?q=${query}`, { signal: controller.signal })
        .then(res => res.json())
        .then(data => callback(data))
        .catch(() => callback());
}

Кэширование результатов загрузки

Для повышения производительности Tom Select может использовать кэширование результатов запросов. Повторные обращения с одинаковым query могут не вызывать новый HTTP-запрос.

Поведение кэша:

  • хранение результатов по ключу запроса;
  • мгновенная отдача при повторном вводе;
  • уменьшение нагрузки на сервер;
  • ускорение интерфейса.

Пример логики кэша:

const cache = {};

load: function(query, callback) {
    if (cache[query]) {
        callback(cache[query]);
        return;
    }

    fetch(`/api?q=${query}`)
        .then(res => res.json())
        .then(data => {
            cache[query] = data;
            callback(data);
        });
}

Пагинация и догрузка данных

События загрузки часто используются совместно с механикой пагинации. Вместо получения полного списка система возвращает ограниченный набор элементов.

Типичная реализация:

load: function(query, callback) {
    fetch(`/api?q=${query}&limit=20`)
        .then(res => res.json())
        .then(data => callback(data));
}

При необходимости догрузки применяются дополнительные параметры:

  • offset;
  • page;
  • cursor.

События загрузки в этом случае повторяются при каждом новом запросе страницы.


Интеграция с поисковым вводом

Загрузка данных тесно связана с событием ввода (type). Каждый ввод может инициировать новый запрос.

Цепочка выглядит так:

  • type → изменение текста;
  • проверка условий загрузки;
  • запуск load;
  • обновление списка.

При высокой частоте ввода важны:

  • debounce на уровне loadThrottle;
  • минимальная длина запроса;
  • отмена устаревших запросов.

Ошибки формата данных

Даже при успешном HTTP-ответе загрузка может считаться ошибочной, если данные не соответствуют ожидаемому формату.

Ожидаемая структура:

[
    { value: '1', text: 'Option 1' },
    { value: '2', text: 'Option 2' }
]

Некорректные случаи:

  • отсутствует value;
  • отсутствует text;
  • возвращён объект вместо массива.

В таких ситуациях инициируется load_error, даже если сеть работает корректно.


Повторная загрузка и принудительное обновление

Tom Select позволяет повторно инициировать загрузку вручную. Это используется при изменении внешних фильтров или контекста.

tomSelect.clearOptions();
tomSelect.load('new query');

Повторная загрузка вызывает полный цикл событий:

  • новый load;
  • возможный load_error;
  • обновление списка.

Поведение при пустом запросе

Особый случай — пустой query. Поведение зависит от конфигурации:

  • загрузка всех элементов;
  • загрузка популярных значений;
  • отсутствие запроса.
shouldLoad: function(query) {
    return query.length === 0 || query.length > 2;
}

При пустом запросе часто используется preload, который инициирует загрузку сразу при инициализации компонента.


Взаимодействие с состоянием интерфейса

События загрузки влияют на визуальное состояние:

  • отображение spinner;
  • блокировка выбора;
  • временное скрытие старых результатов;
  • плавное обновление списка.

Компонент синхронизирует состояние данных и DOM, минимизируя мерцание и повторный рендер.


Итоговая модель поведения загрузки

Механизм загрузки в Tom Select представляет собой управляемый асинхронный поток, где ключевую роль играют:

  • функция load;
  • событие load;
  • событие load_error;
  • throttle и debounce механизмы;
  • кэширование;
  • защита от гонок запросов.

Эта модель обеспечивает устойчивую работу при частых изменениях ввода, нестабильной сети и больших объёмах данных, сохраняя предсказуемость состояния компонента.