FetchListV2.make

Возвращает AsyncGenerator, позволяющий обрабатывать данные из list-методов REST API Bitrix24 версии 2 по мере поступления, не загружая весь массив в память сразу. Особенно полезно при работе с очень большими объёмами данных.
Bitrix24 постепенно переходит на REST API версии 3.

Обзор

Когда нужно обработать большие объёмы данных из list-методов REST API версии 2 по частям (чанками), используйте FetchListV2.make().

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

// Базовое использование
import { EnumCrmEntityTypeId } from '@bitrix24/b24jssdk'

const generator = $b24.actions.v2.fetchList.make({
  method: 'crm.item.list',
  params: {
    entityTypeId: EnumCrmEntityTypeId.deal,
    filter: { '>opportunity': 10000 }
  },
  idKey: 'id',
  customKeyForResult: 'items',
  requestId: 'unique-request-id'
})

for await (const chunk of generator) {
  // Обработка чанка (например, сохранение в БД, анализ и т.д.)
  console.log(`Processing ${chunk.length} items`)
}

Когда использовать FetchListV2.make()

  1. Очень большие объёмы данных: когда число записей в тысячах или десятках тысяч.
  2. Потоковая обработка: когда данные нужно обрабатывать по мере поступления.
  3. Длительные операции: когда обработка каждой записи требует значительного времени.

Сигнатура метода

make<T = unknown>(
  options: ActionFetchListV2
): AsyncGenerator<T[]>

Параметры

Объект options содержит следующие свойства:

ПараметрТипОбязательныйОписание
methodstringДаИмя метода REST API, возвращающего список данных (например, crm.contact.list, tasks.task.list).
paramsOmit<TypeCallParams, 'start' | 'order'>НетПараметры запроса без параметров start и order. Параметр start зарезервирован, так как метод получает все данные одним вызовом. Параметр order зарезервирован, так как курсорной пагинации нужна строгая сортировка по cursorIdKey (по умолчанию равен idKey) по возрастанию — см. Ограничения. Для сужения выборки используйте filter и select.
idKeystringНетИмя поля id так, как оно приходит в каждом элементе ответа; его значение управляет курсором. По умолчанию: 'ID' (верхний регистр). Для методов, возвращающих id в нижнем/camelCase (например, tasks.task.list возвращает id), задайте 'id'.
cursorIdKeystringНетИмя поля, используемое в запросе для order и постраничного фильтра >. По умолчанию равно idKey. Задавайте, только когда имя сортируемого / фильтруемого поля отличается от поля в ответе — например, tasks.task.list сортирует и фильтрует по ID, но возвращает id: передайте idKey: 'id', cursorIdKey: 'ID'.
customKeyForResultstringНетКлюч, по которому в ответе REST API будет выбран массив данных. Например: items для списка элементов CRM.
requestIdstringНетУникальный идентификатор запроса для отслеживания. Используется для дедупликации запросов и отладки.

Возвращаемое значение

AsyncGenerator<T[]> — асинхронный генератор, возвращающий чанки данных в виде массивов типа T.

Каждая итерация генератора возвращает:

  • Массив элементов (чанк) до 50 записей.
  • Генератор завершается, когда все данные получены.

Ключевые понятия

Автоматическое предупреждение

При вызове методов, которые доступны в REST API версии 3, система автоматически выводит предупреждение:

"The method {method_name} is available in restApi:v3. It's worth migrating to the new API."

Это указывает на то, что стоит рассмотреть миграцию на более новую версию REST API.

AsyncGenerator vs Promise

В отличие от CallListV2.make(), который возвращает Promise со всеми данными сразу, FetchListV2.make() возвращает асинхронный генератор:

АспектCallListV2.make()FetchListV2.make()
Возвращаемое значениеPromise<Result<T[]>>AsyncGenerator<T[]>
ПамятьЗагружает все данные в памятьОбрабатывает данные по частям
Использованиеawait response.getData()for await (const chunk of generator)
Подходит дляМалые и средние объёмы данныхОчень большие объёмы данных

Оптимизация производительности

Метод реализует рекомендованный Bitrix24 алгоритм для эффективной работы с большими объёмами данных:

  1. start: -1: отключает подсчёт общего числа записей, значительно ускоряет выполнение запроса.
  2. Фильтрация по возрастающему id: каждый следующий запрос использует фильтр >cursorIdKey с id последнего полученного элемента (читается через idKey).
  3. Потоковая обработка: данные обрабатываются по мере поступления, экономя память.
  4. Автоматическое определение конца данных: запросы останавливаются при получении пустого массива или если число элементов меньше размера страницы (50).

Структура ответа REST API v2

В REST API версии 2 разные методы могут возвращать данные в разных структурах:

  • Прямой массив: { result: [...] }
  • Сгруппированный массив: { result: { items: [...] } }

Параметр customKeyForResult позволяет указать ключ, по которому расположены данные в ответе.

Ограничения

  • Размер страницы: фиксированное ограничение Bitrix24 REST API версии 2 — 50 записей на запрос.
  • Сортировка фиксирована: метод всегда сортирует по cursorIdKey (по умолчанию равен idKey) по возрастанию, потому что курсорная пагинация опирается на фильтр >cursorIdKey для прохождения по набору. Пользовательское значение order нарушило бы этот инвариант, поэтому сигнатура типа исключает order, а любое значение, переданное в runtime, отбрасывается с записью warning в лог. Для сужения выборки используйте filter.
  • Только для list-методов: предназначен только для методов, возвращающих массивы данных.

Обработка ошибок

Поскольку метод возвращает асинхронный генератор, ошибки обрабатываются иначе, чем в CallListV2.make():

import { SdkError } from '@bitrix24/b24jssdk'

try {
  const generator = $b24.actions.v2.fetchList.make({
    method: 'some.method',
    params: { /* some_params */ },
    idKey: 'id',
    customKeyForResult: 'items',
    requestId: 'unique-request-id'
  })

  for await (const chunk of generator) {
    // Обработка чанка (например, сохранение в БД, анализ и т.д.)
    console.log(`Processing ${chunk.length} items`)
  }
} catch (error) {
  // Обработка ошибки
  if (
    error instanceof SdkError
    && error.code === 'JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V2'
  ) {
    console.error(`${error.message}`, { code: error.code })
  } else {
    console.error('Some error', error)
  }
}

Примеры

Поэтапная обработка большого числа компаний

ProcessCrmItems.ts
import { B24Hook, EnumCrmEntityTypeId, LoggerFactory, Text, SdkError, AjaxError } from '@bitrix24/b24jssdk'

type Company = {
  id: number
  title: string
}

const devMode = !!(typeof import.meta !== 'undefined' && (import.meta.dev || import.meta.env?.DEV))
const $logger = LoggerFactory.createForBrowser('Example:ProcessCrmItems', devMode)
const $b24 = B24Hook.fromWebhookUrl('https://your_domain.bitrix24.com/rest/1/webhook_code/')

async function processCrmItemList(): Promise<void> {
  let batchNumber = 0
  let totalItems = 0

  const sixMonthAgo = new Date()
  sixMonthAgo.setMonth((new Date()).getMonth() - 6)
  sixMonthAgo.setHours(0, 0, 0)
  
  const requestId = 'some-crm-item-list'
  
  try {
    const generator = $b24.actions.v2.fetchList.make<Company>({
      method: 'crm.item.list',
      params: {
        entityTypeId: EnumCrmEntityTypeId.company,
        filter: {
          // фильтрация по названию
          '=%title': 'Prime%',
          '>=createdTime': Text.toB24Format(sixMonthAgo) // созданы как минимум 6 месяцев назад
        },
        select: ['id', 'title']
      },
      idKey: 'id',
      customKeyForResult: 'items',
      requestId
    })
    
    for await (const chunk of generator) {
      batchNumber++
      totalItems += chunk.length

      $logger.info(`Processing batch #${batchNumber}`, {
        batchSize: chunk.length,
        totalSoFar: totalItems
      })
      
      // Пример: сохранение в базу данных
      await saveToDatabase(chunk)
      
      // Пример: отправка в очередь сообщений
      await sendToMessageQueue(chunk)
    }
    
    $logger.notice(`Processed ${totalItems} elements in ${batchNumber} batches`)
  } catch (error) {
    if (error instanceof SdkError) {
      $logger.error(`Processing error: ${error.message}`, {
        code: error.code,
        batchNumber,
        totalItems
      })
    } else {
      $logger.error('Unknown error', { error, batchNumber, totalItems })
    }
    throw error
  }
}

// Вспомогательные функции
// Реализация сохранения в базу данных
async function saveToDatabase(items: Company[]): Promise<void> {
  await new Promise(resolve => setTimeout(resolve, 100)) // Simulation
}

// Реализация отправки в очередь сообщений
async function sendToMessageQueue(items: Company[]): Promise<void> {
  await new Promise(resolve => setTimeout(resolve, 50)) // Simulation
}

// Использование
try {
  await processCrmItemList()
} catch (error) {
  $logger.critical('A problem occurred', { error })
}

Пагинация tasks.task.list (поле запроса ≠ поле ответа)

tasks.task.list сортирует и фильтрует по ID (верхний регистр), но возвращает каждую задачу с id в нижнем регистре. Задайте idKey на поле ответа, а cursorIdKey — на поле запроса: курсор тогда читает последний id со страницы и запрашивает следующий с >ID:

ProcessAllTasks.ts
type TaskListItem = { id: string, title: string }

const generator = $b24.actions.v2.fetchList.make<TaskListItem>({
  method: 'tasks.task.list',
  params: {
    filter: { RESPONSIBLE_ID: 1 }, // поля фильтра задач в верхнем регистре
    select: ['ID', 'TITLE'] // выбираем в верхнем регистре, но в ответе id / title в нижнем
  },
  idKey: 'id', // читаем task.id из ответа
  cursorIdKey: 'ID', // order + постраничный фильтр ">ID" в запросе
  customKeyForResult: 'tasks'
})

let total = 0
for await (const chunk of generator) {
  total += chunk.length
}
console.log(`Processed ${total} tasks`)

Без cursorIdKey значение idKey: 'ID' по умолчанию невозможно прочитать из ответа в нижнем регистре, поэтому пагинация остановится после первых 50 задач — и SDK залогирует warning, указывающий сюда.

Альтернативы и рекомендации

  • Размер чанка: Bitrix24 REST API версии 2 всегда возвращает до 50 записей на запрос.
  • Оптимальная параллельность: при обработке данных из генератора рекомендуется ограниченная параллельность (3–5 одновременных операций).
  • Обработка ошибок: всегда обрабатывайте ошибки внутри цикла for await...of, чтобы предотвратить прерывание всего процесса.
  • Мониторинг прогресса: внедряйте логирование прогресса для длительных операций.
  • Для последовательных запросов: используйте Call для одиночных вызовов.
  • Для пакетных операций: используйте Batch для выполнения до 50 команд в одном запросе.
  • На стороне клиента (браузер): используйте встроенный объект B24Frame.
  • См. также: Фильтрация — построение filter (и почему order отбрасывается).