Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда при выполнении «тяжелого» кода на клиенте интерфейс программы замирает, а динамические надписи или индикаторы состояния (например, «Файл загружается...») обновляются только тогда, когда весь процесс уже завершен. Пользователь при этом видит зависшее окно, что создает ощущение зависания программы. В этой статье мы подробно разберем, почему так происходит, и рассмотрим четыре эффективных способа решения этой проблемы на управляемых формах 1С v8.3.
Для начала проанализируем физиологию управляемого интерфейса 1С. Когда выполняется процедура на встроенном языке на клиенте, платформа работает в одном потоке. Все изменения визуальных свойств элементов формы (такие как изменение свойства Заголовок у декорации-надписи) не отрисовываются на экране мгновенно. Они лишь помещаются в очередь отложенного обновления.
Перерисовка интерфейса происходит в так называемом главном цикле обработки событий (idle loop), когда поток выполнения освобождается и управление возвращается самой платформе. Если сразу после изменения текста вы запускаете длительный синхронный процесс (например, чтение тяжелого файла Excel), поток остается занят, и платформа просто физически не успевает перерисовать форму. Вызов метода ОбновитьОтображениеДанных() в данном случае не гарантирует мгновенного рендеринга, особенно в веб-клиенте или тонком клиенте под нагрузкой.
Разберем один из важнейших стандартов разработки БСП и платформы 1С. Начинающие разработчики часто используют элемент формы Декорация-надпись для вывода динамически изменяющегося текста. Однако стандарт разработки 1С гласит: декорации предназначены исключительно для статического текста (пояснений, подсказок), который не меняется в коде.
Для вывода динамически меняющейся информации всегда следует использовать Поле формы с видом Поле надписи. Посмотрим, почему это важно:
ПутьКДанным (например, с реквизитом ТекстСтатуса типа Строка).Заголовок декорации, которое может инициировать полный пересчет разметки формы.Давайте посмотрим на правильную настройку элемента формы по шагам:
ТекстИнфо с типом Строка.ТекстИнфо = "Новый статус";.Если нам необходимо выполнить действие немедленно после изменения текста на форме, отличным решением является «разрыв» контекста выполнения с помощью метода ПодключитьОбработчикОжидания(). Это классический паттерн для обхода синхронной блокировки UI.
Суть метода заключается в следующем: мы меняем текст на форме, запускаем однократный таймер на минимальное время (например, 0.1 секунды) и немедленно завершаем текущую процедуру. Контроль возвращается платформе, она успевает перерисовать интерфейс, после чего срабатывает таймер и запускает нашу тяжелую процедуру.
Рассмотрим пример кода, реализующий этот подход:
&НаКлиенте
Процедура ВыбратьИЗагрузитьФайл(Команда)
// 1. Изменяем статус на форме (используем реквизит формы ТекстИнфо)
ТекстИнфо = "Выполняется подготовка к загрузке...";
Элементы.КартинкаЗагрузка.Картинка = БиблиотекаКартинок.Ожидание;
// 2. Подключаем обработчик ожидания на 0.1 секунды с признаком однократности (Истина)
ПодключитьОбработчикОжидания("Подключаемый_ВыполнитьВыборИЗагрузку", 0.1, Истина);
// 3. Завершаем процедуру. Управление возвращается платформе, интерфейс мгновенно перерисовывается!
КонецПроцедуры
&НаКлиенте
Процедура Подключаемый_ВыполнитьВыборИЗагрузку()
ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ДиалогОткрытияФайла.ПолноеИмяФайла = "";
ДиалогОткрытияФайла.Заголовок = "Выберите файл для загрузки";
Фильтр = "Microsoft Excel(*.xls;*.xlsx)|*.xls;*.xlsx";
ДиалогОткрытияФайла.Фильтр = Фильтр;
Если ДиалогОткрытияФайла.Выбрать() Тогда
ПутьКФайлу = ДиалогОткрытияФайла.ПолноеИмяФайла;
// Меняем статус перед тяжелой загрузкой
ТекстИнфо = "Файл выбран. Выполняется загрузка данных...";
// Снова используем небольшой разрыв контекста, чтобы текст успел обновиться перед тяжелым чтением файла
ПараметрыЗапуска = Новый Структура("ПутьКФайлу", ПутьКФайлу);
ПодключитьОбработчикОжидания("Подключаемый_ТяжелаяЗагрузка", 0.1, Истина);
Иначе
ТекстИнфо = "Выбор файла отменен";
Элементы.КартинкаЗагрузка.Картинка = Новый Картинка;
КонецЕсли;
КонецПроцедуры
Благодаря такому каскадному вызову обработчиков ожидания, пользователь видит плавную смену статусов, а интерфейс приложения остается отзывчивым перед каждым важным шагом.
В современной платформе 1С (начиная с версии 8.3.15) синхронные методы работы с файлами, такие как Выбрать() у объекта ДиалогВыбораФайла или метод ПоместитьФайл(), являются устаревшими. В веб-клиенте они заблокированы полностью, а в тонком клиенте их использование считается плохим тоном и может приводить к ошибкам.
Асинхронные методы работают по принципу обратного вызова (Callback) или через обещания (Promises / Описания Оповещения). При вызове асинхронного метода выполнение кода приостанавливается, управление возвращается платформе (в этот момент интерфейс гарантированно перерисовывается), а управление возвращается в отдельную процедуру только тогда, когда пользователь сделал свой выбор.
Проанализируем пример асинхронного выбора файла и его последующей загрузки на сервер:
&НаКлиенте
Процедура НачатьВыборФайла(Команда)
ТекстИнфо = "Ожидание выбора файла...";
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Фильтр = "Файлы Excel (*.xlsx)|*.xlsx";
// Создаем описание оповещения, которое сработает после выбора файла
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
// Вызываем асинхронный метод вместо синхронного Диалог.Выбрать()
Диалог.Показать(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайла(РезультатВыбора, ДополнительныеПараметры) Экспорт
Если РезультатВыбора = Неопределено Тогда
ТекстИнфо = "Выбор файла отменен пользователем.";
Возврат;
КонецЕсли;
ПутьКФайлу = РезультатВыбора[0];
ТекстИнфо = "Файл выбран. Начинается загрузка на сервер...";
// Асинхронно помещаем файл во временное хранилище
ОписаниеОповещенияЗагрузки = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайлаНаСервер(ОписаниеОповещенияЗагрузки,,,, ПутьКФайлу, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(РезультатЗагрузки, ДополнительныеПараметры) Экспорт
Если РезультатЗагрузки = Неопределено Или Не РезультатЗагрузки.ФайлПомещен Тогда
ТекстИнфо = "Ошибка при загрузке файла.";
Возврат;
КонецЕсли;
ТекстИнфо = "Файл успешно передан на сервер. Запуск обработки данных...";
// Вызываем серверный метод для парсинга и обработки файла
ОбработатьФайлНаСервере(РезультатЗагрузки.Адрес);
ТекстИнфо = "Обработка данных завершена!";
КонецПроцедуры
Этот подход не только решает проблему своевременного обновления статусов на форме, но и делает вашу конфигурацию полностью кроссплатформенной (код будет одинаково стабильно работать и в Тонком клиенте, и в браузере под Веб-клиентом).
Если обработка файла (например, парсинг Excel на 10 000 строк) занимает значительное время (более 5-10 секунд), то даже при использовании асинхронных методов интерфейс программы «зависнет» во время выполнения серверного вызова. Пользователь не сможет нажать другие кнопки, а операционная система может пометить окно 1С как «Не отвечает».
Для таких случаев в составе Библиотеки стандартных подсистем (БСП) существует мощный механизм «Длительные операции». Он позволяет выполнять тяжелую работу на сервере в фоновом задании, а на клиенте отображать интерактивное окно ожидания с индикатором («бубликом») и возможностью отмены.
Рассмотрим общую схему реализации длительной операции по шагам:
ДлительныеОперации.ЗапуститьВыполнениеВФоне().ДлительныеОперацииКлиент.ОткрытьФормуОжидания(). Данная форма сама опрашивает сервер о состоянии выполнения процесса.ДлительныеОперации.СообщитьПрогресс() для отправки информации о проценте выполнения или текущей строке обратно на клиент. Окно ожидания будет динамически показывать этот прогресс пользователю!Ниже приведен пример реализации запуска фонового задания на сервере через БСП:
&НаСервере
Функция ЗапуститьОбработкуФайлаВФоне(АдресФайла)
ПараметрыПроцедуры = Новый Структура;
ПараметрыПроцедуры.Вставить("АдресФайла", АдресФайла);
НаименованиеЗадания = "Парсинг и загрузка данных из файла";
// Запускаем фоновое задание через БСП
Результат = ДлительныеОперации.ЗапуститьВыполнениеВФоне(
УникальныйИдентификатор,
"МойОбщийМодульСервер.ОбработатьЗагруженныйФайл",
ПараметрыПроцедуры,
НаименованиеЗадания
);
Возврат Результат;
КонецФункции
На клиенте после получения структуры Результат мы открываем форму ожидания, передавая в нее идентификатор задания. Это гарантирует профессиональный, отзывчивый и стабильный интерфейс, соответствующий лучшим практикам разработки фирмы «1С».
Подводя итог, выделим ключевые правила работы с динамическими элементами управляемой формы 1С: