Маршрутизация в Google Maps JavaScript API строится вокруг сервиса Google Maps JavaScript API DirectionsService, который выполняет запросы к серверу маршрутов, и DirectionsRenderer, отвечающего за визуализацию результата. Несмотря на стабильность платформы Google, запросы маршрутов подвержены множеству факторов отказа: от некорректных входных данных до ограничений квот и сетевых сбоев.
Корректная обработка ошибок маршрутизации является обязательной частью архитектуры клиентских картографических приложений, поскольку напрямую влияет на устойчивость интерфейса и предсказуемость поведения системы.
Ошибки при построении маршрута возникают на нескольких уровнях:
1. Ошибки входных данных
2. Ограничения сервиса
3. Логические ограничения маршрута
4. Сетевые ошибки
Результат запроса DirectionsService содержит поле status, которое определяет итог выполнения операции.
Наиболее важные значения:
OK Маршрут успешно построен, данные доступны в результате.
NOT_FOUND Одна или обе точки не могут быть геокодированы.
ZERO_RESULTS Маршрут между точками отсутствует.
MAX_WAYPOINTS_EXCEEDED Превышено допустимое количество промежуточных точек.
MAX_ROUTE_LENGTH_EXCEEDED Маршрут слишком длинный для расчёта.
INVALID_REQUEST Ошибка в структуре запроса (например, отсутствуют обязательные параметры).
OVER_QUERY_LIMIT Превышен лимит запросов API-ключа.
REQUEST_DENIED Запрос отклонён (ключ недействителен или отсутствуют права доступа).
UNKNOWN_ERROR Временная ошибка сервера, часто повторяемая при последующих попытках.
Классическая схема обработки ошибок строится на анализе status в callback функции DirectionsService:
const directionsService = new google.maps.DirectionsService();
const directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);
const request = {
origin: "Moscow",
destination: "Saint Petersburg",
travelMode: google.maps.TravelMode.DRIVING
};
directionsService.route(request, (result, status) => {
if (status === "OK") {
directionsRenderer.setDirections(result);
return;
}
if (status === "ZERO_RESULTS") {
console.error("Маршрут не найден между точками");
return;
}
if (status === "NOT_FOUND") {
console.error("Одна из точек не может быть определена");
return;
}
console.error("Ошибка маршрутизации:", status);
});
Данный подход обеспечивает минимально необходимую обработку, но не покрывает повторные попытки и сетевые сбои.
В современных версиях Google Maps JavaScript API используется асинхронный API через Promise:
const directionsService = new google.maps.DirectionsService();
async function calculateRoute() {
const request = {
origin: "Berlin",
destination: "Munich",
travelMode: google.maps.TravelMode.DRIVING
};
try {
const result = await directionsService.route(request);
if (result.status !== "OK") {
throw new Error(result.status);
}
directionsRenderer.setDirections(result);
} catch (error) {
console.error("Ошибка при построении маршрута:", error.message);
}
}
Promise-форма упрощает композицию логики, особенно при цепочках fallback-стратегий.
Ситуация ZERO_RESULTS означает отсутствие доступного маршрута при заданных параметрах, но не всегда означает полную невозможность навигации.
Практическая стратегия обработки:
async function safeRoute(request) {
let response = await directionsService.route(request);
if (response.status === "ZERO_RESULTS") {
const fallbackRequest = {
...request,
travelMode: google.maps.TravelMode.WALKING,
waypoints: []
};
response = await directionsService.route(fallbackRequest);
}
return response;
}
OVER_QUERY_LIMIT возникает при превышении квоты запросов. Простое повторение запроса без задержки приводит к каскадным ошибкам.
Используется стратегия экспоненциальной задержки:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function routeWithRetry(request, retries = 3) {
for (let i = 0; i < retries; i++) {
const result = await directionsService.route(request);
if (result.status === "OK") {
return result;
}
if (result.status !== "OVER_QUERY_LIMIT") {
throw new Error(result.status);
}
await sleep(Math.pow(2, i) * 500);
}
throw new Error("Превышен лимит запросов");
}
Такая схема снижает нагрузку и увеличивает вероятность успешного ответа при кратковременных перегрузках.
Эти ошибки относятся к системным и не подлежат повтору запроса без изменения параметров.
REQUEST_DENIED
INVALID_REQUEST
Пример строгой валидации перед запросом:
function validateRequest(req) {
if (!req.origin || !req.destination) {
throw new Error("origin и destination обязательны");
}
if (!req.travelMode) {
throw new Error("travelMode не указан");
}
}
UNKNOWN_ERROR считается временной серверной ошибкой. Обычно она требует повторного запроса с задержкой.
Рекомендуемая стратегия:
В сложных приложениях целесообразно выносить обработку статусов в отдельный слой:
function handleDirectionsStatus(status) {
switch (status) {
case "OK":
return { ok: true };
case "ZERO_RESULTS":
return { ok: false, message: "Маршрут не найден" };
case "NOT_FOUND":
return { ok: false, message: "Точка не найдена" };
case "OVER_QUERY_LIMIT":
return { ok: false, retry: true };
case "REQUEST_DENIED":
return { ok: false, fatal: true, message: "Доступ запрещён" };
case "INVALID_REQUEST":
return { ok: false, fatal: true, message: "Некорректный запрос" };
default:
return { ok: false, retry: true, message: status };
}
}
Такой слой позволяет унифицировать реакцию приложения на любые сценарии отказа.
Для стабильной эксплуатации системы маршрутизации важно фиксировать:
Пример структуры логирования:
function logRouteError(request, status) {
console.log({
origin: request.origin,
destination: request.destination,
travelMode: request.travelMode,
status: status,
timestamp: Date.now()
});
}
Такая информация критична для анализа деградации сервиса и выявления проблем с квотами или геоданными.
Практически используемые приложения комбинируют несколько подходов:
Подобная архитектура делает слой маршрутизации устойчивым к сетевым и сервисным сбоям, сохраняя предсказуемость поведения интерфейса даже при деградации внешнего API.