В архитектуре событийной модели Google Maps JavaScript API ключевую роль играет возможность регистрации обработчиков, которые автоматически удаляются после первого срабатывания. Такой подход используется для сценариев, где повторная реакция на событие не требуется: инициализация карты, первичная загрузка тайлов, получение геолокации, однократная подстройка интерфейса.
Событийная система построена поверх пространства
google.maps.event, которое предоставляет методы подписки и
управления жизненным циклом обработчиков. В классической модели
используются addListener и removeListener,
однако для одноразовых сценариев предусмотрен специализированный
механизм — addListenerOnce.
Постоянные обработчики остаются активными до явного удаления:
const listener = map.addListener("click", (e) => {
console.log("Клик по карте", e.latLng);
});
// удаление при необходимости
google.maps.event.removeListener(listener);
Одноразовый обработчик автоматически удаляется после первого вызова:
map.addListenerOnce("click", (e) => {
console.log("Сработает только один раз", e.latLng);
});
Ключевое отличие заключается не только в удобстве синтаксиса, но и в управлении памятью: исключается необходимость ручного вызова удаления.
Метод addListenerOnce фактически оборачивает стандартный
обработчик, добавляя промежуточную функцию-декоратор. После первого
вызова происходит:
Такой механизм особенно важен в долгоживущих SPA-приложениях, где утечки памяти приводят к накоплению неиспользуемых замыканий.
Однократное ожидание полной загрузки состояния карты:
map.addListenerOnce("idle", () => {
console.log("Карта полностью загружена и стабилизирована");
});
Событие idle срабатывает после завершения всех
трансформаций viewport, включая загрузку тайлов и завершение
анимаций.
map.addListenerOnce("bounds_changed", () => {
const bounds = map.getBounds();
console.log("Границы получены", bounds);
});
Использование одноразового обработчика предотвращает многократные вызовы при серии изменений масштаба.
marker.addListenerOnce("dragend", (e) => {
console.log("Маркер перемещён один раз", e.latLng);
});
Несмотря на автоматическое удаление, одноразовые события участвуют в
общей системе управления слушателями. Объект, возвращаемый при подписке,
остаётся идентичным стандартному MVCEventListener, что
позволяет унифицировать подход к логированию и отладке.
| Характеристика | addListener | addListenerOnce |
|---|---|---|
| Количество вызовов | неограниченное | один |
| Требуется удаление | да | нет |
| Риск утечек | выше | минимальный |
| Использование памяти | стабильное при удалении | автоматически очищается |
Одноразовые события часто используются в связке с асинхронными операциями, где необходимо дождаться первого стабильного состояния карты:
function waitForMapIdle(map) {
return new Promise((resolve) => {
map.addListenerOnce("idle", () => {
resolve("ready");
});
});
}
Такой паттерн превращает событийную модель в управляемый поток выполнения.
При работе с динамическими изменениями (zoom, center, bounds) одноразовые обработчики позволяют фиксировать конкретный этап взаимодействия:
map.setZoom(10);
map.addListenerOnce("zoom_changed", () => {
console.log("Zoom установлен и применён");
});
Это исключает повторные вызовы при инерционных изменениях масштаба.
В долгоживущих приложениях ключевой проблемой становится накопление
подписок. Даже при использовании removeListener возможны
ситуации, когда ссылки на callback сохраняются в замыканиях.
Одноразовые события решают проблему на уровне API: после вызова происходит разрыв цепочки ссылок внутри диспетчера событий. Это снижает вероятность:
Внутри google.maps.event одноразовые обработчики
реализуются через обёртку:
function once(instance, eventName, handler) {
const wrapper = (event) => {
google.maps.event.removeListener(listener);
handler(event);
};
const listener = instance.addListener(eventName, wrapper);
return listener;
}
Подобная схема демонстрирует принцип: удаление происходит до или сразу после выполнения callback.
Если внутри одноразового callback возникает исключение, удаление слушателя всё равно выполняется, поскольку отписка происходит на уровне wrapper-функции. Это предотвращает повторное срабатывание даже при аварийном завершении логики.
При комбинировании нескольких одноразовых событий важно учитывать порядок их срабатывания:
map.addListenerOnce("tilesloaded", () => {
map.addListenerOnce("idle", () => {
console.log("Сначала тайлы, затем стабилизация");
});
});
Такая композиция позволяет строить последовательные цепочки состояний карты без внешнего менеджера событий.
Одноразовые события часто используются при синхронизации с внешними API:
fetch("/geo.json")
.then(res => res.json())
.then(() => {
map.addListenerOnce("idle", () => {
console.log("Данные загружены и карта стабилизирована");
});
});
Здесь событие выступает как точка фиксации состояния после завершения загрузки данных и рендеринга.
Повторный вызов addListenerOnce создаёт независимый
обработчик. Каждый из них сработает один раз, независимо от других
подписок:
map.addListenerOnce("click", () => console.log("A"));
map.addListenerOnce("click", () => console.log("B"));
При первом клике оба обработчика будут активированы и затем удалены.
Официальная модель событий описана в документации платформы Google Maps: