Common.Platform.AsyncFallback — Универсальный helper для таймаутов асинхронных операций

Автор: Art Yg
Версия: 1.0

AsyncFallback — это платформенный helper, который решает одну практическую проблему:
как безопасно обработать ситуацию “мы отправили async-запрос, но ответ может не прийти вовремя или вообще не прийти”.

Он нужен для любых операций, где есть “PHASE 1 → отправили запрос” и “PHASE 2 → пришёл callback”:

AsyncFallback не является AI-модулем. Это оркестрация платформенного уровня.


Зачем он существует

В реальном мире async-вызовы ломаются не потому что “код плохой”, а потому что:

Без таймаута сценарий может залипнуть: пользователь пишет, а система “ждёт” бесконечно.

AsyncFallback решает это через стандартный механизм Metabot Scheduler:

  1. Планирует job: “через N секунд выполнить fallback-script”
  2. Отменяет job, когда callback успешно пришёл
  3. Позволяет фиксировать причину ошибки и детали, чтобы дальше можно было принять решение (редирект, retry, сообщение пользователю)

Основные принципы


Минимальные требования

Чтобы использовать AsyncFallback, нужно:

  1. Иметь доступ к планировщику Metabot:
    • bot.scheduleJob({ lead_id, script_code, run_after_sec })
    • bot.clearJobsByScriptCode(script_code, lead_id)
  2. Иметь leadId (или возможность вычислить lead_id)

AsyncFallback не требует базы данных, таблиц или дополнительных сервисов.


Концепция Namespace

Namespace — обязательный “scope” для идентификации конкретной операции.

Это важно, потому что на одном lead могут одновременно идти:

Если бы мы хранили “timeout.script” без namespace — они бы перетирали друг друга.

Пример namespace:


Конфигурация

AsyncFallback конфигурируется на вызове:

Поля конфигурации


Как использовать

PHASE 1 — отправка async-запроса (isFirstImmediateCall)

  1. Конфигурируем fallback
  2. Планируем таймаут
  3. Отправляем remote request
  4. Возвращаем false (ждём callback)
const AsyncFallback = require("Common.Platform.AsyncFallback");

const fb = AsyncFallback.configure({
  lead,
  namespace: "orion_image_reflection",
  timeout: { seconds: 120, script: "Orion_Image_Timeout" },
  error: { flagAttr: "orion_image_error", reasonAttr: "orion_image_error_reason" }
});

fb.schedule();

// ... RemoteApiCall.send(..., asyncResponse: true)
return false;

PHASE 2 — пришёл callback

  1. Снимаем таймаут (если он был)
  2. Валидируем результат
  3. Если контракт нарушен — fail(reason, details)
  4. Дальше ты решаешь: редиректить в error-script или вернуться “в ту же точку”
const AsyncFallback = require("Common.Platform.AsyncFallback");

const fb = AsyncFallback.configure({
  lead,
  namespace: "orion_image_reflection"
});

fb.unschedule();

// если ответ плохой (например нет url при requireUrl=true)
fb.fail("url_missing", { requireUrl: true });

// твоя стратегия выхода:
return bot.run({ script_code: "Orion_Image_Error" });

Методы

AsyncFallback.configure(params) → instance

Создаёт instance и сохраняет конфиг в lead под namespace.
Главная точка входа. Используй и в PHASE 1, и в PHASE 2 — единообразно.


instance.schedule() → boolean

Планирует fallback-job, если timeout.seconds и timeout.script заданы.

Поведение:


instance.unschedule() → boolean

Отменяет запланированный fallback-job (если он был).

Поведение:


instance.fail(reason, details?) → true

Фиксирует ошибку и причину:

Это не редирект и не exception — это маркер, после которого ты сам решаешь, что делать.


instance.clear() → true

Очищает служебные данные namespace:

Полезно после успешного завершения операции (опционально).


Типовой паттерн: “пользователь пишет, пока ждём”

AsyncFallback — про таймаут, но он закрывает важный кусок UX:

Обычно это делается так:

(Эта логика живёт в конкретном плагине типа ImageGen/LLMQuery, а AsyncFallback даёт им общий таймаутный механизм.)


Когда использовать


Когда не использовать


Практические замечания, чтобы не словить редкий ад

Поздний callback после таймаута

Может случиться: таймаут-скрипт уже отработал, а потом всё же прилетел success-callback.
AsyncFallback снимает job на callback, но если job уже выполнился, снять уже нечего.

Правильный паттерн на уровне компонента:

Это не обязанность AsyncFallback, потому что стратегия зависит от бизнес-логики.


Итог

Common.Platform.AsyncFallback — простой, но критически полезный слой платформенной оркестрации:


Версия #1
Artem Garashko создал 1 February 2026 10:47:36
Artem Garashko обновил 1 February 2026 10:50:21