Работа с асинхронностью

Mapbox GL JS построен вокруг событийно-асинхронной архитектуры, где почти все операции, связанные с картой, источниками данных и стилями, выполняются не синхронно, а через очередь загрузки и внутренние обработчики состояния. Это обусловлено тем, что отображение карты зависит от множества внешних ресурсов: тайлов, шрифтов, спрайтов, изображений, конфигураций стиля и данных источников.

Асинхронность в данной библиотеке не является вспомогательной особенностью, а формирует основу жизненного цикла карты.


Инициализация карты и отложенная готовность

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

Ключевой момент — событие загрузки стиля:

  • load — фиксирует момент, когда базовый стиль и обязательные ресурсы загружены
  • idle — сигнализирует, что отсутствуют активные сетевые запросы и рендеринг завершён

Асинхронная модель проявляется сразу: доступ к слоям и источникам до наступления load приводит к непредсказуемому поведению, поскольку внутренняя очередь операций ещё не синхронизирована с состоянием карты.


Событийная модель как основа асинхронности

Вся работа карты строится через события:

  • загрузка данных
  • обновление источников
  • изменение стиля
  • завершение рендеринга
  • загрузка тайлов

Наиболее важные события:

  • style.load — завершение загрузки стиля
  • styledata — частичное обновление структуры стиля
  • sourcedata — поступление данных из источников
  • dataloading — начало загрузки данных
  • render — каждый кадр рендеринга

Каждое событие отражает состояние внутреннего асинхронного конвейера, где данные проходят несколько стадий: запрос → загрузка → парсинг → рендер.


Асинхронная загрузка стиля

Стиль карты представляет собой JSON-конфигурацию, содержащую слои, источники, спрайты и шрифты. Его загрузка включает несколько параллельных операций:

  • загрузка JSON стиля
  • загрузка sprite sheet (иконок)
  • загрузка glyphs (шрифтов)
  • инициализация источников данных

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

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


Источники данных и асинхронная подгрузка тайлов

Источники (sources) в Mapbox GL JS являются основным механизмом получения геоданных. Каждый тип источника имеет собственную модель асинхронности:

Vector Tile Source

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

  • масштаба
  • позиции камеры
  • области видимости

Каждый тайл представляет собой отдельный HTTP-запрос, выполняемый лениво.

Raster Source

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

GeoJSON Source

GeoJSON может быть:

  • синхронным (передан напрямую)
  • асинхронным (загружен по URL)

В случае URL выполняется fetch, после чего данные проходят парсинг и индексацию.


Promise-обёртки и загрузка изображений

Несмотря на событийную модель, часть API предоставляет Promise-интерфейсы.

Загрузка изображений:

  • map.loadImage(url, callback)
  • современный вариант возвращает Promise

Процесс полностью асинхронен:

  1. сетевой запрос изображения
  2. декодирование
  3. преобразование в формат, совместимый с WebGL
  4. регистрация в контексте карты

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


Асинхронное добавление слоёв и источников

Методы addSource и addLayer формально синхронны, но фактически включаются в очередь операций рендеринга.

Причины асинхронного поведения:

  • слой не может быть отрисован до завершения загрузки стиля
  • источник может быть ещё не загружен
  • WebGL контекст может находиться в состоянии инициализации

Внутренне операции откладываются до ближайшего безопасного цикла рендеринга.


Очередь рендеринга и requestAnimationFrame

Рендеринг карты основан на цикле requestAnimationFrame. Это означает:

  • обновление состояния происходит в пределах кадров браузера
  • изменения группируются перед отрисовкой
  • несколько операций могут быть объединены в один рендер

Асинхронность здесь проявляется как батчинг изменений: множество вызовов API не приводит к мгновенному перерисовыванию.


Загрузка спрайтов и шрифтов

Спрайты и glyphs являются критическими визуальными ресурсами, но загружаются отдельно от основного стиля.

  • Sprite: JSON + PNG или WebP
  • Glyphs: растровые шрифты в формате PBF

Асинхронные особенности:

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

Обработка состояния загрузки источников

Каждый источник данных проходит через несколько асинхронных стадий:

  • loading — инициирован запрос
  • loaded — данные получены
  • tile ready — тайлы декодированы
  • render-ready — данные доступны GPU

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


Конкурентность и управление запросами

При перемещении карты может возникать большое количество параллельных запросов тайлов. Для управления этим используются:

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

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


Асинхронные ошибки и их обработка

Ошибки загрузки ресурсов обрабатываются через события:

  • error — общий обработчик
  • ошибки тайлов
  • ошибки источников
  • ошибки стиля

Типичная особенность — отсутствие исключений в синхронном коде. Ошибки почти всегда проявляются позже, в асинхронных callback-цепочках.


Динамическая смена стиля

Смена стиля является одной из наиболее сложных асинхронных операций:

  1. очистка текущих источников
  2. загрузка нового JSON стиля
  3. параллельная загрузка sprite/glyphs
  4. пересборка слоёв
  5. повторная инициализация источников

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


Асинхронная работа с пользовательскими данными

При добавлении пользовательских GeoJSON или внешних данных через fetch-цепочки возникает типичный поток:

  • запрос данных через fetch
  • преобразование в GeoJSON
  • добавление через addSource
  • привязка слоя
  • ожидание рендера

Каждый этап зависит от завершения предыдущего, формируя цепочку промисов.


Web Workers и фоновые вычисления

Значительная часть асинхронной нагрузки выносится в Web Workers:

  • обработка векторных тайлов
  • триангуляция геометрии
  • подготовка данных для WebGL

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


Параллелизм тайлов и управление видимой областью

При изменении камеры происходит перерасчёт:

  • какие тайлы нужны
  • какие можно удалить
  • какие следует обновить

Этот процесс полностью асинхронен и зависит от:

  • zoom level
  • bounding box
  • скорости перемещения

Система пытается предсказать потребности и заранее загружать тайлы, создавая эффект упреждающей асинхронной загрузки.


Синхронизация состояния через idle

Событие idle является ключевым маркером стабилизации:

  • отсутствуют активные сетевые запросы
  • завершены все текущие рендеры
  • нет незавершённых обновлений источников

Однако даже в этом состоянии новые асинхронные операции могут быть инициированы сразу после взаимодействия с картой, что возвращает систему в активный цикл.


Итеративная модель обновления кадра

Каждый кадр рендеринга проходит через последовательность:

  1. сбор изменений состояния
  2. проверка источников
  3. загрузка недостающих данных
  4. подготовка WebGL буферов
  5. отрисовка

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