Надёжность картографического приложения во многом зависит от корректной обработки ошибок. В Mapbox GL JS ошибки могут возникать на разных уровнях: при инициализации карты, загрузке стилей, получении тайлов, работе с источниками данных, обработке пользовательских событий и взаимодействии с внешними API. Грамотная стратегия обработки ошибок позволяет своевременно выявлять проблемы, предотвращать сбои интерфейса и обеспечивать предсказуемое поведение приложения.
Наиболее распространённые категории ошибок:
Пример проблемной инициализации:
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/invalid-style-id'
});
В данном случае карта не сможет загрузить указанный стиль.
Основным механизмом обработки ошибок является событие
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);
});
Одной из самых частых причин ошибок является неверный токен доступа.
Некорректная настройка:
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 постоянно загружает векторные и растровые тайлы.
Проблемы могут возникать из-за:
Диагностика:
map.on('error', ({ error }) => {
console.error(error);
});
Для мониторинга сетевой активности полезно использовать инструменты разработчика браузера:
Перед работой с источником необходимо удостовериться в его наличии.
Опасный вариант:
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');
}
Некоторые операции удобнее защищать стандартным механизмом обработки исключений JavaScript.
try {
map.addSource('cities', {
type: 'geojson',
data: geojson
});
} catch (error) {
console.error(error);
}
Особенно актуально при работе с динамическими данными.
Повреждённый 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);
}
}
Проверка кода ответа позволяет своевременно обнаруживать проблемы сервера.
При работе с внешними сервисами возможны повреждённые ответы.
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);
});
Популярные категории отслеживаемых событий:
Централизованный мониторинг позволяет обнаруживать проблемы ещё до появления массовых обращений пользователей и существенно упрощает сопровождение картографических приложений на базе Mapbox GL JS.