Колбэки и обработчики

В библиотеке STOMP.js вся модель взаимодействия строится вокруг событийно-ориентированного подхода, где основной поток управления передаётся через набор колбэков и обработчиков, реагирующих на жизненный цикл соединения, подписок и входящих сообщений. Такой подход позволяет отделить транспортный уровень (WebSocket/STOMP) от бизнес-логики приложения и строить масштабируемые системы обмена сообщениями.

STOMP-клиент представляет собой конечный автомат, проходящий через состояния подключения, активной работы и отключения. Каждое состояние сопровождается набором событий, которые перехватываются через функции-обработчики.

Ключевая особенность модели заключается в том, что практически любое действие в STOMP.js реализуется через callback:

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

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

Колбэк подключения: onConnect

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

const client = new StompJs.Client({
  brokerURL: "ws://localhost:15674/ws",

  onConnect: (frame) => {
    console.log("Соединение установлено");
  }
});

Содержимое frame

Аргумент frame содержит данные протокольного ответа сервера:

  • заголовки соединения
  • session-id
  • информацию о брокере
  • дополнительные метаданные

Этот объект используется для диагностики и адаптации поведения клиента под серверные условия.

Обработчик сообщений подписки

Основной поток данных в STOMP.js поступает через подписки. Каждая подписка получает callback, который вызывается при поступлении сообщения.

const subscription = client.subscribe("/topic/orders", (message) => {
  console.log(message.body);
});

Структура message объекта

Объект сообщения включает:

  • body — полезная нагрузка (строка или сериализованный JSON)
  • headers — метаданные сообщения
  • ack — функция подтверждения доставки (если включён режим ack)

Преобразование данных

Часто тело сообщения требует десериализации:

client.subscribe("/topic/orders", (message) => {
  const data = JSON.parse(message.body);
  processOrder(data);
});

Колбэк выполняет роль централизованной точки обработки входящего потока данных.

Подтверждение доставки (ack callback)

При использовании режима подтверждения вручную (client-ack) появляется дополнительный уровень управления обработкой сообщений.

client.subscribe(
  "/queue/tasks",
  (message) => {
    try {
      handleTask(JSON.parse(message.body));
      message.ack();
    } catch (e) {
      message.nack();
    }
  },
  { ack: "client" }
);

Поведение ack/nack

  • ack() — подтверждает успешную обработку
  • nack() — сигнализирует о необходимости повторной доставки

Эти методы являются частью callback-цепочки обработки и напрямую влияют на семантику доставки сообщений.

Обработчик ошибок: onStompError

Ошибки протокольного уровня STOMP обрабатываются через отдельный callback.

const client = new StompJs.Client({
  brokerURL: "ws://localhost:15674/ws",

  onStompError: (frame) => {
    console.error("STOMP ошибка:", frame.headers["message"]);
    console.error("Детали:", frame.body);
  }
});

Типы ошибок

Чаще всего обрабатываются:

  • ошибки аутентификации
  • отказ в подписке
  • ошибки брокера
  • нарушения протокола STOMP

Этот callback не связан с транспортными ошибками WebSocket, что важно для разделения уровней диагностики.

Обработчик низкоуровневых ошибок: onWebSocketError

Ошибки на уровне транспортного соединения обрабатываются отдельно.

client.onWebSocketEr ror = (event) => {
  console.error("WebSocket ошибка:", event);
};

Особенности

Этот callback вызывается при:

  • обрыве соединения
  • сетевых сбоях
  • недоступности сервера
  • ошибках TLS/SSL

Он не содержит STOMP-данных и работает на уровне сырого канала связи.

Обработчик закрытия соединения: onDisconnect / onWebSocketClose

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

client.onWebSocketCl ose = () => {
  console.log("Соединение закрыто");
};

Практическое значение

Этот callback используется для:

  • очистки ресурсов
  • остановки подписок
  • инициирования переподключения
  • сохранения состояния приложения

Он завершает жизненный цикл активного подключения.

Callback переподключения: reconnectDelay и lifecycle hooks

STOMP.js поддерживает автоматическое переподключение, управляемое параметрами клиента.

const client = new StompJs.Client({
  reconnectDelay: 5000
});

Хотя это не callback в строгом смысле, поведение переподключения часто сопровождается дополнительными обработчиками состояния.

Дополнительные события состояния

client.onConn ect = () => {
  console.log("Подключено");
};

client.onDisconn ect = () => {
  console.log("Отключено");
};

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

Обработчики подписок и управление жизненным циклом

Каждая подписка в STOMP.js возвращает объект, содержащий метод unsubscribe, который также может быть обёрнут логикой callback-управления.

const sub = client.subscribe("/topic/updates", handler);

// позже
sub.unsubscribe();

Типовые сценарии использования

  • временные подписки (UI компоненты)
  • контекстные подписки (страница/модуль)
  • динамическая маршрутизация сообщений

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

Обработка потоков сообщений через единый диспетчер

В сложных приложениях callback подписки часто заменяется диспетчеризацией:

client.subscribe("/topic/events", (message) => {
  const event = JSON.parse(message.body);

  switch (event.type) {
    case "create":
      handleCreate(event);
      break;
    case "update":
      handleUpdate(event);
      break;
    case "delete":
      handleDelete(event);
      break;
  }
});

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

Промежуточные обработчики и композиция callback-ов

Callback-архитектура STOMP.js позволяет строить цепочки обработки:

const parse = (msg) => JSON.parse(msg.body);
const validate = (data) => data && data.id;

client.subscribe("/topic/data", (message) => {
  const data = parse(message);

  if (!validate(data)) return;

  process(data);
});

Принципы композиции

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

Асинхронные callback-и

Хотя callback подписки выглядит синхронным, он часто содержит асинхронные операции:

client.subscribe("/topic/jobs", async (message) => {
  const job = JSON.parse(message.body);
  await processAsync(job);
});

Особенности:

  • STOMP.js не ожидает завершения callback
  • ошибки внутри async требуют явного catch
  • возможны гонки обработки сообщений

Ошибки внутри callback-ов

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

client.subscribe("/topic/test", (message) => {
  throw new Error("Ошибка обработки");
});

Такие ошибки необходимо оборачивать вручную:

client.subscribe("/topic/test", (message) => {
  try {
    handle(message);
  } catch (e) {
    console.error("Ошибка в обработчике:", e);
  }
});

Callback как основа реактивной модели

Вся система STOMP.js фактически является реактивной:

  • входящие события → callback подписки
  • изменения состояния → lifecycle callbacks
  • ошибки → error handlers
  • управление ресурсами → disconnect callbacks

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