Асинхронность в управляемом интерфейсе 1С
1С на управляемых формах теперь работает на большом количестве платформ – браузеры, мобильные телефоны, Windows, Mac, Linux.
Поэтому некоторые вещи пришлось исключать из 1С, чтобы обеспечить возможность выполнения на разных платформах. Среди этих вещей – синхронность выполнения.
Теперь код 1С должен писаться для асинхронного выполнения. Некоторые ошибочно воспринимают этот факт только в том ключе, что теперь в 1С запрещены модальные окна. Ничего подобного, модальные окна (блокирование всего интерфейса) по-прежнему доступны.
Асинхронность – это совершенно другое, оно затронуло даже то, что не касается интерфейса.
Синхронный и асинхронный код
При выполнении любого кода есть моменты, когда программа занимается ожиданием завершения длительной операции – реакции пользователя, распечатки документа, завершения поиска файлов в каталоге, ответа от интернет-сервера.
В синхронном коде выполняется цикл ожидания завершения длительной операции:
Процедура Работа()
//Код до длительной операции
ВыполнитьДлительнуюОперацию();
Пока НЕ ОжиданиеЗавершено() Цикл
КонецЦикла
//Код после длительной операции
КонецПроцедуры
В асинхронном коде выполнение отдается системе при старте длительной операции и возвращается назад программе при завершении этой операции:
Процедура Работа()
//Код до длительной операции
ВыполнитьДлительнуюОперацию(ПродолжениеРаботы);
КонецПроцедуры
Процедура ПродолжениеРаботы ()
//Код после длительной операции
КонецПроцедуры
В асинхронном коде появляется дробление кода на множество процедур. В-принципе, этого можно было бы избежать неявной генерацией процедур из кода. Т.е. код можно было бы писать как и раньше, система 1С сама бы генерировала процедуры на низком уровне:
Процедура Работа()
//Код до длительной операции
//На низком уровне завершал бы выполнение процедуры Работа
ВыполнитьДлительнуюОперацию(ПродолжениеРаботы);
//Код после длительной операции, выносился бы в отдельную процедуру на низком уровне
КонецПроцедуры
Процедура Работа может содержать локальные переменные, и вызов длительной процедуры может происходить из середины цикла.
Следовательно, 1С для возрождения контекста процедуры после возвращения выполнения из системы должна восстановить все локальные переменные, переменные цикла и войти так, чтобы оказаться на нужной итерации цикла.
1с не реализовала неявное преобразование обычного кода в асинхронный, переложив тяжесть реализации асинхронности на программиста.
Необходимость преобразования кода из синхронного в асинхронный связана с тем, что в некоторых платформах и системах нельзя крутить бесконечный цикл с вызовом DoEvents. Например, в браузерах выполнение кода должно завершаться в ограниченные сроки.
Асинхронное выполнение циклов
Асинхронное выполнение снижает читабельность и восприятие программы, провоцирует разбиение дробление кода на мелкие участки.
В синхронном режиме цикл можно было написать так:
Для Инд = 1 По Всего Цикл
ВыполнитьДлительнуюОперацию();
ВыполнитьДлительнуюОперацию2();
//Операторы после длительных операций
КонецЦикла;
В асинхронном режиме цикл придется переписать так:
Перем мИнд; //Теперь глобальная переменная
…
мИнд = 0; //Как вариант мИнд = Неопределено;
ИтерацияЦиклаИнд();
…
Процедура ИтерацияЦиклаИнд()
мИнд = мИнд + 1; //Как вариант мИнд = ? (мИнд = Неопределено, 1, мИнд + 1);
Если мИнд > Всего Тогда
Возврат;
КонецЕсли;
ВыполнитьДлительнуюОперацию(ВыполнитьДлительнуюОперациюЗавершение);
КонецПроцедуры
Процедура ВыполнитьДлительнуюОперациюЗавершение()
ВыполнитьДлительнуюОперацию2(ВыполнитьДлительнуюОперацию2Завершение);
КонецПроцедуры
Процедура ВыполнитьДлительнуюОперацию2Завершение()
//Операторы после длительной операций
ИтерацияЦиклаИнд (); //Аналог оператора Продолжить
КонецПроцедуры
Видно, что нужно явно прописывать вызов следующей итерации. Особенно это важно, если используется цепочка длительных операций, например: сначала получить от пользователя выбор файла, затем получить данные о нем из операционной системы, и т.п.
Переменная Инд для упрощения кода инкрементируется в процедуре итерации до вызова первой длительной операции. Поэтому Инд стартует со значения на единице меньше начального. Как вариант, можно инкрементировать Инд после сравнения с всего, а далее использовать не переменную мИнд, а переменную текущего элемента:
Если мИнд > Всего Тогда
Возврат;
КонецЕсли;
мТекущийЭлемент = мТЗ[Инд];
мИнд = мИнд + 1;
Модальные формы
Что касается модальных форм, то это лишь один из примеров асинхронности. Пожалуй, самый доступный.
Когда вызывается модальная форма, то в асинхронной реализации мы должны прекратить выполнение кода 1с.
Выполнение кода возобновляется, когда пользователь закрыл модальное окно и передается в процедуру, назначенную как обработчик оповещения этого события.
На самом деле в управляемых формах 1С есть остались модальные окна, это окна которые показываются в режиме «Блокировка всего интерфейса», просто они обрабатываются асинхронным способом.
Плоды асинхронности
Увеличение объема кода
Список процедур, которые требуют асинхронного выполнения, большой. Это диалоги с пользователем, поиск файлов в каталоге, выполнение команд системы с ожиданием возврата.
Соответственно, код будет часто разбиваться на маленькие кусочки, дробиться на процедуры. Что не будет способствовать прозрачности и удобству восприятия кода.
С другой стороны, программиста все больше и больше приучают к клиент-серверному программированию в парадигме – «послал запрос – жди ответ».
Т.е. классическое программирование последовательного выполнения кода в 1С изживает себя, полностью ломая восприятие программиста для программирования под клиент-серверную архитектуру.
Код в очередной раз усложнился без видимых преимуществ для программиста.
Однако ничего не поделаешь, с этим придется жить и страдать. Интересно, что никакие другие системы кроме 1с не переложили реализацию асинхронности на программиста.
Сохранение и восстановление контекста выполнения
Если раньше код мог целиком выполняться на сервере, то теперь, при необходимости получить атрибуты файла или другой длительной операции, придется сохранять текущие рассчитанные на сервере данные в хранилище. Причем сохранять не до следующего вызова сервера, а до востребования, потому что до момента, когда они понадобятся, сервер может быть вызван для других операций. Затем их восстанавливать и продолжать на очередной итерации. Т.е. накладные расходы увеличиваются. С другой стороны, по сравнению с длительным ожиданием результата операции накладные расходы по восстановлению контекста составляют крайне небольшой процент.
Но программисту, так или иначе, придется заботиться о сохранении контекста, что повлечет необходимость написания дополнительного кода. Очередное увеличение трудозатрат.
Независающие формы
Переход на асинхронный код приведет к тому, что даже без фоновых задач можно будет выполнять несколько операций одновременно.
Допустим, была обработка, которая в цикле обрабатывала документы. Управляемая форма передавала управление на сервер и там выполнялась обработка. В асинхронном варианте придется обработать документ и дождаться возвращения управления от системы. Т.е. после передачи вызова в систему выполнение кода обработки прекратится до получения ответа и пользователь сможет в этом же приложении выполнять другие действия.
Пример асинхронного кода
Я написал обработку, которая выдает список файлов в каталоге. В отличии от типовой, можно обрабатывая отдельно подпапки, чтобы ненужные можно было исключить из результатов. Обработка поиска полностью асинхронная, попробуйте сравнить с обычной.