Как правильно реализовать асинхронную загрузку данных и ускорить открытие формы в 1С?

Программист 1С v8.3 (Обычные формы) Торговля и дистрибуция
← На главную

При разработке сложных интерфейсов в 1С программисты часто сталкиваются с проблемой долгого открытия форм. Типичная ситуация: при открытии списка номенклатуры необходимо рассчитать дополнительные параметры, цены или остатки. Если делать это синхронно в процедуре ПриОткрытии или ПередОткрытием, интерфейс «замирает», и пользователь вынужден ждать несколько секунд. В этой статье мы разберем, почему ключевое слово Асинх не всегда спасает ситуацию, и как правильно перенести тяжелые расчеты в фоновый поток.

Почему ключевое слово Асинх не ускоряет расчеты?

Рассмотрим распространенное заблуждение: многие разработчики полагают, что использование конструкции Асинх Функция автоматически создает новый поток исполнения (как в некоторых других языках программирования). Проанализируем пример кода, который часто приводит к разочарованию:


&НаКлиенте
Асинх Процедура КнопкаВыполнитьНажатие(Кнопка)
    // Ожидается, что это выполнится параллельно
    Результат = ВывестиСообщениеОЗначенииИтератораАсинх();                                  
    
    Сообщить("123");
    Сообщить(Ждать Результат);
КонецПроцедуры 

&НаКлиенте
Асинх Функция ВывестиСообщениеОЗначенииИтератораАсинх()
    Сч = 0;
    Пока Сч < 1000000 Цикл
        Сч = Сч + 1;    
    КонецЦикла;
    Возврат("Значение итератора = " + Сч);          
КонецФункции

В данном случае код все равно будет выполняться синхронно. Выясним причину: в 1С клиентское приложение работает в одном потоке. Ключевое слово Асинх — это лишь способ описать логику ожидания внешних событий (например, открытия окна или ответа от веб-сервиса) без блокировки интерфейса. Однако тяжелые математические циклы или формирование таблиц значений внутри такой функции по-прежнему занимают основной поток. Пока цикл Пока ... Цикл не завершится, 1С не перейдет к строке Сообщить("123").

Важный вывод: асинхронные функции предназначены для неблокирующего ожидания (оператор Ждать), а не для параллельных вычислений на клиенте.

Решение проблемы через фоновые задания

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

Разберем алгоритм реализации для обычных форм по шагам:

  1. В событии формы ПриОткрытии запускаем фоновое задание на сервере.
  2. Используем ПоместитьВоВременноеХранилище, чтобы передать параметры для расчета в фоновое задание.
  3. На клиенте подключаем обработчик ожидания через ПодключитьОбработчикОжидания.
  4. В обработчике ожидания проверяем статус задания. Если оно завершено — забираем результат из временного хранилища и заполняем реквизиты формы.

Посмотрим на пример реализации запуска задания:


Процедура ПриОткрытии()
    // Создаем уникальный адрес для получения результата
    АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, ЭтаФорма.УникальныйИдентификатор);
    
    // Запускаем расчет на сервере
    МассивПараметров = Новый Массив;
    МассивПараметров.Добавить(АдресРезультата);
    ФоновыеЗадания.Выполнить("МойОбщийМодуль.РассчитатьДанныеДляФормы", МассивПараметров);
    
    // Включаем проверку готовности каждые 2 секунды
    ПодключитьОбработчикОжидания("ПроверитьЗавершениеРасчета", 2);
КонецПроцедуры

В серверном методе РассчитатьДанныеДляФормы мы выполняем запрос и помещаем результат обратно в хранилище по переданному адресу. Таким образом, форма открывается сразу, пользователь может просматривать список, а через пару секунд данные в таблице обновятся автоматически.

Оптимизация тяжелого запроса

Часто причиной задержек является не только синхронность, но и неэффективный текст запроса. Рассмотрим пример запроса автора темы и проанализируем критические ошибки производительности. Проблема заключается в использовании виртуальных таблиц СрезПоследних внутри ЛЕВОЕ СОЕДИНЕНИЕ.

Почему это медленно? Когда вы соединяете справочник напрямую с виртуальной таблицей среза без параметров, СУБД может пытаться сначала рассчитать срез для ВСЕХ записей регистра, а потом соединять их. Это крайне ресурсоемкая операция.

Рассмотрим, как оптимизировать данный запрос:


// Вместо прямого соединения со срезом в цикле:
// 1. Выгружаем нужные ссылки во временную таблицу (ВТ)
// 2. Получаем срез последних, используя ВТ в качестве фильтра в параметрах виртуальной таблицы

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Номенклатура ИЗ Справочник.Номенклатура ГДЕ ...;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|   Цены.Цена,
|   Курсы.Курс,
|   ВТ.Ссылка
|ИЗ
|   ВТ_Номенклатура КАК ВТ
|   ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура В (ВЫБРАТЬ Ссылка ИЗ ВТ_Номенклатура)) КАК Цены
|   ПО ВТ.Ссылка = Цены.Номенклатура";

Обратите внимание на использование фильтра Номенклатура В (ВЫБРАТЬ Ссылка ИЗ ВТ_Номенклатура) внутри параметров СрезПоследних. Это заставляет систему выбирать данные только по тем записям, которые реально нужны для текущей формы.

Дополнительные рекомендации по работе с Таблицей Значений

Проанализируем еще несколько важных технических моментов:

Таким образом, для решения задачи автора необходимо отказаться от попыток использовать Асинх для вычислений и перевести формирование ТаблицыЗначений в фоновое задание, попутно оптимизировав структуру запроса к регистрам сведений.

← На главную