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с не переложили реализацию асинхронности на программиста.
Если раньше код мог целиком выполняться на сервере, то теперь, при необходимости получить атрибуты файла или другой длительной операции, придется сохранять текущие рассчитанные на сервере данные в хранилище. Причем сохранять не до следующего вызова сервера, а до востребования, потому что до момента, когда они понадобятся, сервер может быть вызван для других операций. Затем их восстанавливать и продолжать на очередной итерации. Т.е. накладные расходы увеличиваются. С другой стороны, по сравнению с длительным ожиданием результата операции накладные расходы по восстановлению контекста составляют крайне небольшой процент.
Но программисту, так или иначе, придется заботиться о сохранении контекста, что повлечет необходимость написания дополнительного кода. Очередное увеличение трудозатрат.
Переход на асинхронный код приведет к тому, что даже без фоновых задач можно будет выполнять несколько операций одновременно.
Допустим, была обработка, которая в цикле обрабатывала документы. Управляемая форма передавала управление на сервер и там выполнялась обработка. В асинхронном варианте придется обработать документ и дождаться возвращения управления от системы. Т.е. после передачи вызова в систему выполнение кода обработки прекратится до получения ответа и пользователь сможет в этом же приложении выполнять другие действия.
Я написал обработку, которая выдает список файлов в каталоге. В отличии от типовой, можно обрабатывая отдельно подпапки, чтобы ненужные можно было исключить из результатов. Обработка поиска полностью асинхронная, попробуйте сравнить с обычной.