Error handling

Надёжность картографического приложения во многом зависит от корректной обработки ошибок. В Mapbox GL JS ошибки могут возникать на разных уровнях: при инициализации карты, загрузке стилей, получении тайлов, работе с источниками данных, обработке пользовательских событий и взаимодействии с внешними API. Грамотная стратегия обработки ошибок позволяет своевременно выявлять проблемы, предотвращать сбои интерфейса и обеспечивать предсказуемое поведение приложения.


Источники возникновения ошибок

Наиболее распространённые категории ошибок:

  • некорректный Access Token;
  • недоступность сетевых ресурсов;
  • ошибки загрузки стиля карты;
  • повреждённые GeoJSON-данные;
  • ошибки при работе с источниками и слоями;
  • попытки обращения к несуществующим объектам;
  • исключения внутри пользовательских обработчиков событий;
  • ошибки интеграции со сторонними сервисами.

Пример проблемной инициализации:

const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/invalid-style-id'
});

В данном случае карта не сможет загрузить указанный стиль.


Событие error

Основным механизмом обработки ошибок является событие error.

map.on('error', (event) => {
    console.error('Map error:', event.error);
});

Объект события содержит информацию о возникшей проблеме:

map.on('error', (event) => {
    console.log(event);
});

Часто используемые свойства:

Свойство Описание
event.error Объект ошибки
event.type Тип события
event.target Экземпляр карты

Вывод подробной диагностики:

map.on('error', ({ error }) => {
    console.error('Message:', error.message);
    console.error('Stack:', error.stack);
});

Проверка корректности Access Token

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

Некорректная настройка:

mapboxgl.accessToken = 'invalid-token';

При загрузке карты может появиться ошибка авторизации.

Обработка:

map.on('error', ({ error }) => {
    if (error && error.status === 401) {
        console.error('Invalid access token');
    }
});

Дополнительно рекомендуется проверять наличие токена ещё до создания карты.

if (!mapboxgl.accessToken) {
    throw new Error('Mapbox access token is missing');
}

Отслеживание ошибок загрузки стиля

Карта полностью зависит от загружаемого стиля.

Пример:

const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/example/nonexistent'
});

Для контроля загрузки:

map.on('error', ({ error }) => {
    console.error('Style loading error:', error);
});

Дополнительная проверка успешной загрузки:

map.on('style.load', () => {
    console.log('Style loaded successfully');
});

Обработка ошибок загрузки тайлов

Mapbox GL JS постоянно загружает векторные и растровые тайлы.

Проблемы могут возникать из-за:

  • отсутствия интернет-соединения;
  • ошибок сервера;
  • неправильных URL;
  • ограничений доступа.

Диагностика:

map.on('error', ({ error }) => {
    console.error(error);
});

Для мониторинга сетевой активности полезно использовать инструменты разработчика браузера:

  • Network;
  • Console;
  • Performance.

Проверка существования источников

Перед работой с источником необходимо удостовериться в его наличии.

Опасный вариант:

map.getSource('cities').setData(data);

Если источник отсутствует, возникнет исключение.

Безопасный вариант:

const source = map.getSource('cities');

if (source) {
    source.setData(data);
}

Либо:

if (!map.getSource('cities')) {
    console.error('Source not found');
    return;
}

Проверка существования слоя

Такая же проблема характерна для слоёв.

Небезопасный код:

map.removeLayer('buildings');

Без проверки:

if (map.getLayer('buildings')) {
    map.removeLayer('buildings');
}

Для удаления слоя и источника:

if (map.getLayer('buildings')) {
    map.removeLayer('buildings');
}

if (map.getSource('buildings')) {
    map.removeSource('buildings');
}

Защита от повторного добавления слоёв

Попытка добавить слой с существующим идентификатором приводит к ошибке.

Проблемный пример:

map.addLayer({
    id: 'roads',
    type: 'line',
    source: 'roads'
});

Если слой уже существует:

if (!map.getLayer('roads')) {
    map.addLayer({
        id: 'roads',
        type: 'line',
        source: 'roads'
    });
}

Проверка готовности карты

Многие ошибки возникают из-за обращения к карте до завершения загрузки.

Неверно:

map.addLayer(layerConfig);

Сразу после создания объекта карты.

Правильно:

map.on('load', () => {
    map.addLayer(layerConfig);
});

Также доступна проверка:

if (map.isStyleLoaded()) {
    console.log('Style is ready');
}

Использование try…catch

Некоторые операции удобнее защищать стандартным механизмом обработки исключений JavaScript.

try {
    map.addSource('cities', {
        type: 'geojson',
        data: geojson
    });
} catch (error) {
    console.error(error);
}

Особенно актуально при работе с динамическими данными.


Валидация GeoJSON

Повреждённый GeoJSON является распространённой причиной ошибок.

Пример некорректной структуры:

{
    "type": "FeatureCollection",
    "features": null
}

Проверка:

function validateGeoJSON(data) {
    return (
        data &&
        data.type === 'FeatureCollection' &&
        Array.isArray(data.features)
    );
}

Использование:

if (!validateGeoJSON(geojson)) {
    console.error('Invalid GeoJSON');
    return;
}

Обработка ошибок загрузки внешних данных

Получение GeoJSON через Fetch требует отдельного контроля ошибок.

async function loadData() {
    try {
        const response = await fetch('/data/cities.geojson');

        if (!response.ok) {
            throw new Error(
                `HTTP error: ${response.status}`
            );
        }

        const data = await response.json();

        map.getSource('cities').setData(data);

    } catch (error) {
        console.error('Loading failed:', error);
    }
}

Проверка кода ответа позволяет своевременно обнаруживать проблемы сервера.


Обработка ошибок JSON-парсинга

При работе с внешними сервисами возможны повреждённые ответы.

try {
    const data = JSON.parse(responseText);
} catch (error) {
    console.error('JSON parsing failed');
}

Для асинхронного кода:

try {
    const response = await fetch(url);
    const data = await response.json();
} catch (error) {
    console.error(error);
}

Глобальный перехват необработанных ошибок

Для крупных приложений полезно фиксировать все необработанные исключения.

window.addEventListener('error', (event) => {
    console.error('Global error:', event.error);
});

Обработка ошибок промисов:

window.addEventListener(
    'unhandledrejection',
    (event) => {
        console.error(
            'Unhandled promise rejection:',
            event.reason
        );
    }
);

Логирование ошибок

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

Простейший логгер:

function logError(error, context = '') {
    console.error({
        timestamp: new Date().toISOString(),
        context,
        message: error.message,
        stack: error.stack
    });
}

Использование:

try {
    updateMap();
} catch (error) {
    logError(error, 'updateMap');
}

Создание пользовательских ошибок

Для повышения читаемости кода можно создавать собственные типы исключений.

class MapDataError extends Error {
    constructor(message) {
        super(message);
        this.name = 'MapDataError';
    }
}

Применение:

if (!geojson.features.length) {
    throw new MapDataError(
        'GeoJSON contains no features'
    );
}

Перехват:

try {
    loadGeoJSON();
} catch (error) {
    if (error instanceof MapDataError) {
        console.error(error.message);
    }
}

Отображение ошибок пользователю

Не все ошибки должны оставаться только в консоли.

Пример уведомления:

function showError(message) {
    document.getElementById('status').textContent =
        message;
}

Использование:

try {
    await loadData();
} catch {
    showError(
        'Не удалось загрузить картографические данные'
    );
}

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


Безопасная работа с событиями

Ошибки внутри обработчиков могут нарушать работу приложения.

map.on('click', (event) => {
    try {
        processClick(event);
    } catch (error) {
        console.error(error);
    }
});

Особенно важно для сложной бизнес-логики.


Обработка ошибок при работе с маркерами

Создание маркеров на основе внешних данных требует проверки координат.

Проблемный вариант:

new mapboxgl.Marker()
    .setLngLat(item.coordinates)
    .addTo(map);

Безопасный вариант:

if (
    Array.isArray(item.coordinates) &&
    item.coordinates.length === 2
) {
    new mapboxgl.Marker()
        .setLngLat(item.coordinates)
        .addTo(map);
}

Проверка диапазона координат

Дополнительная валидация помогает избежать некорректного отображения объектов.

function isValidCoordinate(lng, lat) {
    return (
        lng >= -180 &&
        lng <= 180 &&
        lat >= -90 &&
        lat <= 90
    );
}

Применение:

if (!isValidCoordinate(lng, lat)) {
    throw new Error('Invalid coordinates');
}

Защита асинхронных операций

Асинхронные процессы часто становятся источником трудноуловимых ошибок.

async function updateSource() {
    try {
        const response = await fetch('/api/data');

        if (!response.ok) {
            throw new Error('Network error');
        }

        const data = await response.json();

        const source = map.getSource('cities');

        if (!source) {
            throw new Error('Source not found');
        }

        source.setData(data);

    } catch (error) {
        console.error(error);
    }
}

Централизованная система обработки ошибок

В крупных проектах распространён единый обработчик.

class ErrorHandler {

    static handle(error, context) {
        console.error(
            `[${context}]`,
            error
        );
    }

}

Использование:

try {
    initializeMap();
} catch (error) {
    ErrorHandler.handle(
        error,
        'Map initialization'
    );
}

Преимущества такого подхода:

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

Интеграция с сервисами мониторинга

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

Общая схема:

map.on('error', ({ error }) => {
    monitoringService.capture(error);
});

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

  • ошибки загрузки карты;
  • сетевые ошибки;
  • исключения JavaScript;
  • сбои работы API;
  • ошибки обработки GeoJSON;
  • проблемы пользовательских сценариев.

Централизованный мониторинг позволяет обнаруживать проблемы ещё до появления массовых обращений пользователей и существенно упрощает сопровождение картографических приложений на базе Mapbox GL JS.