К содержимому

Документация

TypeScript SDK: Журнал, прогресс и безопасный повтор

Во время длинного сбора пользователь должен понимать, что происходит. ctx.log() добавляет объяснение в журнал, ctx.progress() показывает текущий этап, ctx.metric() собирает числовые показатели. Эти методы локальны и не требуют await.

Понятный журнал

Записывайте начало категории, число найденных страниц, пропуски и причину остановки. Не записывайте пароль, токен, текст личного письма или полное содержимое приватного документа. Для ошибки используйте ctx.fail(reason) с конкретным наблюдением: например, «страница вернула HTTP 403», а не «что-то пошло не так».

Прогресс и отмена

Если известна общая длина, передайте done и total. Если неизвестна, передайте null вместо общего числа и обновляйте название этапа. Не показывайте 100% до завершения сохранения всех записей.

async function run(ctx: Akmelo.Context): Promise<void> {
  const urls = ctx.param('urls', []);
  if (!Array.isArray(urls)) ctx.abort('Укажите список адресов');
  let processed = 0;
  ctx.log('Начинаем обход', { pages: urls.length });
  for (const url of urls) {
    if (await ctx.cancelled()) ctx.abort('Сбор остановлен пользователем');
    const response = await ctx.http().get(String(url));
    if (!response.ok()) ctx.fail(`HTTP ${response.status}: ${url}`);
    processed++;
    ctx.metric('pages_processed', 1);
    ctx.progress(processed, urls.length, 'Загрузка страниц');
  }
  ctx.log('Обход завершён', { processed });
}

Состояние между запусками

await ctx.state().get(key, fallback) читает сохранённый курсор; set() и incr() обновляют его. Состояние не заменяет таблицу результатов. Для товара используйте стабильный ключ ctx.emit(record, key). Повторите пробный запуск с теми же входами и проверьте, что дубликатов и пропусков нет.

async function run(ctx: Akmelo.Context): Promise<void> {
  const state = ctx.state();
  const nextPage = Number(await state.get('next_page', 1));
  const url = ctx.url().withParam(ctx.param<string>('catalog_url'), 'page', nextPage);
  const response = await ctx.http().get(url);
  if (!response.ok()) ctx.fail(`HTTP ${response.status}`);
  const cards = response.dom().qa('.product-card');
  for (const card of cards) {
    const sku = card.attr('data-sku');
    if (sku) ctx.emit({ sku, name: card.text() }, sku);
  }
  if (cards.length) await state.set('next_page', nextPage + 1);
}

Курсор стоит менять после успешного чтения страницы. ctx.flush() отправляет накопленные записи на обработку, но сам по себе не подтверждает их окончательное сохранение. Проверяйте итоговый статус прогона и делайте повтор безопасным за счёт стабильных ключей записей.

Context · KeyValue · Жизненный цикл