Получить консультацию

Синхронные и асинхронные методы в 1С. Рефакторинг.

В данном материале мы рассмотрим отличие синхронных и асинхронных методов, а также приемы рефакторинга на примере нескольких часто используемых действий, таких как вывод предупреждения, вопрос пользователю или ввод пользователем каких-то значений.

Синхронные методы

Строго говоря, практически весь код в 1С – синхронный, т.е. выполняется последовательно оператор за оператором. Например, код:

А = 10;
А = А + 10;
Сообщить (А);

Соответственно, и большая часть методов платформы являются синхронными. Например, мы не можем асинхронно получить выборку из результата запроса или асинхронно свернуть таблицу значений. Проблемы начинают возникать, когда код 1С начинает взаимодействовать с внешним миром – например, с пользователем, с файловой подсистемой, с операционной системой или веб-сервисами. В этом случае платформа начинает ждать, пока внешний мир отреагирует, и выполнение кода на это время приостанавливается.

Например, файловая система занята какой-то тяжелой операцией, и не может сразу обработать нужный файл, или веб-приложение отвечает с задержкой, или пользователь должен ввести значение, но отошел выпить кофе. Платформе остается лишь ждать, и весь код ставится “на паузу”. Разумеется, речь идет о текущем сеансе конкретного пользователя.

Поэтому для обеспечения большей отзывчивости интерфейса разработчики платформы 1С создали асинхронные аналоги, которые не блокируют выполнение кода. К ним относятся например, методы ПоказатьВопрос, НачатьПомещениеФайлаНаСервер, и многие другие. Вместо приостановки выполнения кода асинхронные методы выполняются “в фоне”, и по завершении дают возможность обработать результат в специальном обработчике – “Обработке оповещения”. Такой подход использовался длительное время, да и сейчас в типовых конфигурациях и БСП он много где используется. В последних версиях платформы 1С добавила “синтаксического сахара” и сделала жизнь разработчиков проще, а код – читабельнее. Об этом читайте в соответствующих разделах статьи – про старый и новый подходы.

Синхронность и модальность в 1С - в чем разница?

Иногда возникает путаница в терминологии, но тут все просто. Модальные вызовы – это такое поведение интерфейса, когда вызывается открытие окна, и это одно окно блокирует все остальные. Т.е. помимо того что само выполнение кода поставлено на паузу, еще и интерфейс ждет реакции пользователя. Например, к модальным методам относятся Предупреждение, ОткрытьФормуМодально.

Можно долго дискутировать на предмет того, почему 1С решила отказываться от модальности – частично это связано с развитием UX, созданием более отзывчивого и дружелюбного интерфейса, частично – с технологическими особенностями реализации модальных окон в браузерах. Но имеем данность – в веб-клиенте нам не удастся в 1С открыть окно модально, а в типовых конфигурациях в свойствах конфигурации почти везде стоит “Режим использования модальности – Не использовать”.

Соответственно, наша как разработчика задача – корректно использовать немодальные аналоги привычных методов. Немодальные окна не приостанавливают выполнение основного потока выполнения, но могут блокировать интерфейс либо его часть. Например, окно выбора контрагента может блокировать окно документа-владельца, куда мы хотим подобрать этого контрагента, а все остальное будет доступно.

Асинхронные методы 1С - старый подход

Рассмотрим на примере метода “Предупреждение”. Это модальный синхронный метод глобального контекста. И все мы привыкли, что он выводит окошко с предупреждением, и система ждет таймаута либо пока пользователь нажмет “ОК”. В асинхронном варианте нам следует использовать метод ПоказатьПредупреждение. В случае, если нам нужно, чтобы после предупреждения отработал какой-то код, его следует вынести в экспортную клиентскую процедуру, и указать имя этой процедуры в обработке оповещения.

&НаКлиенте
Процедура ПриОткрытии(Отказ)	
	ПоказатьПредупреждение(Новый ОписаниеОповещения("ПослеПоказаПредупреждения", ЭтотОбъект), "Выведем предупреждение");
КонецПроцедуры

&НаКлиенте
Процедура ПослеПоказаПредупреждения(ДополнительныеПараметры) Экспорт
	А = 2;
	Б = А+2;
КонецПроцедуры

Но такой подход имеет ряд недостатков. Во-первых, код может “размазываться” по нескольким модулям, так как обработка оповещения может находиться в другом клиентском модуле. Во-вторых, с ростом количества идущих подряд асинхронных вызовов количество и сложность кода увеличивается, и его становится трудно читать и неудобно поддерживать.
Например, мы сперва можем задать пользователю вопрос, затем предложить выбор файла для загрузки, после этого снова задать вопрос, после этого – предложить каталог для сохранения файла, и т.п. В результате ,например, для четырех асинхронных методов вместо одной процедуры мы получим восемь, что, согласитесь, не очень читабельно и удобно.

Асинх и Ждать - новый подход

Начиная с версии 8.3.18, в 1С появился новый подход для работы с асинхронностью – добавились ключевые слова Асинх и Ждать, а также появился новый тип – Обещание. Все это очень похоже на аналогичный механизм в JavaScript, и даже названия являются буквально переводом английских терминов Asinc, Await и Promise.

Работает все это примерно так. Мы объявляем метод асинхронным – пишем перед объявлением процедуры или функции слово Асинх. В коде такого метода мы можем использовать ключевое слово Ждать. Если использовать слово Ждать без слова Асинх, получим ошибку “Оператор Ждать (Await) может употребляться только в асинхронных процедурах или функциях”.
Теперь можно писать асинхронные методы в синхронном стиле. Например:

&НаКлиенте
Асинх Процедура ПриОткрытии(Отказ)
	Ждать ПредупреждениеАсинх("Выведем предупреждение");
	//Этот код отработает только после того как отработает асинхронный метод.
	А = 2;
	Б = А+2;
	ПредупреждениеАсинх("Выведем предупреждение");
	//Этот код отработает сразу, т.к. нет слова Ждать
	В = Б+3;
КонецПроцедуры

Важный нюанс. Выполнение асинхронных функций возвращает обещание, в том случае, если функция еще не завершила свою работу. И мы это обещание можем поместить в переменную, передать в какой-нибудь другой метод, и уже там ждать результат.

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

&НаКлиенте
Асинх Процедура ПолучитьРезультатАсинхронно(Команда)
	Сообщить("Начало асинхронного вызова");
	Результат = Ждать АсинхронныйРезультат();
	Сообщить("Конец асинхронного вызова");
	Сообщить("Получили результат асинхронно " + Результат);
КонецПроцедуры

&НаКлиенте
Асинх Функция АсинхронныйРезультат()
	ВводПользователя = Ждать ВвестиЧислоАсинх(0);
	Сообщить("Получили результат " + ВводПользователя);
	Возврат ВводПользователя;
КонецФункции

Приемы рефакторинга

В конфигураторе 1С есть некоторый инструментарий – не сказать чтоб очень богатый, но полезный – для рефакторинга кода. При помощи рефакторинга мы можем быстро преобразовать “старые” синхронные вызовы в асинхронные. Рассмотрим процедуру с использованием синхронных методов:

&НаКлиенте
Процедура ПримерРефакторингаКода(Команда)
	
	Предупреждение("Тут важное сообщение");
	Ответ = Вопрос("Уверены?", РежимДиалогаВопрос.ДаНет);
	Если Ответ = КодВозвратаДиалога.Да Тогда
		Число1 = ВвестиЧисло(0);
		Число2 = ВвестиЧисло(0);
		Результат = Число1*Число2;
		
		Сообщить(Результат);
	Иначе
		Предупреждение("Не уверен - не начинай!");
	КонецЕсли;
	
КонецПроцедуры

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

&НаКлиенте
Процедура ПримерРефакторингаКода(Команда)
	ПоказатьПредупреждение(Новый ОписаниеОповещения("ПримерРефакторингаКодаЗавершение4", ЭтаФорма), "Тут важное сообщение");
КонецПроцедуры

&НаКлиенте
Процедура ПримерРефакторингаКодаЗавершение4(ДополнительныеПараметры) Экспорт
	Ответ = Неопределено;
	ПоказатьВопрос(Новый ОписаниеОповещения("ПримерРефакторингаКодаЗавершение3", ЭтаФорма), "Уверены?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ПримерРефакторингаКодаЗавершение3(РезультатВопроса, ДополнительныеПараметры) Экспорт
	Ответ = РезультатВопроса;
	Если Ответ = КодВозвратаДиалога.Да Тогда
		ПоказатьВводЧисла(Новый ОписаниеОповещения("ПримерРефакторингаКодаЗавершение2", ЭтаФорма), 0);
	Иначе
		ПоказатьПредупреждение(Новый ОписаниеОповещения("ПримерРефакторингаКодаЗавершение", ЭтаФорма), "Не уверен - не начинай!");
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПримерРефакторингаКодаЗавершение2(Число, ДополнительныеПараметры) Экспорт
	Число1 = (Число <> Неопределено);
	ПоказатьВводЧисла(Новый ОписаниеОповещения("ПримерРефакторингаКодаЗавершение1", ЭтаФорма, Новый Структура("Число1", Число1)), 0);
КонецПроцедуры

&НаКлиенте
Процедура ПримерРефакторингаКодаЗавершение1(Число, ДополнительныеПараметры) Экспорт
	Число1 = ДополнительныеПараметры.Число1;
	
	Число2 = (Число <> Неопределено);
	Результат = Число1*Число2;
	
	Сообщить(Результат);
КонецПроцедуры

&НаКлиенте
Процедура ПримерРефакторингаКодаЗавершение(ДополнительныеПараметры) Экспорт
	
КонецПроцедуры

В таком коде достаточно легко запутаться, но увы, в платформе до версии 8.3.18 новые асинхронные методы  недоступны.
Также мы можем преобразовать только один нужный нам метод в асинхронный. Для этого нужно выделить его и в меню Рефакторинг / Нерекомендуемые синхронные вызовы выбрать пункт Преобразовать вызов. В открывшемся окне можно изменить имя обработчика оповещения.

Если мы уже начали писать вызов асинхронного “старого” метода, и нам лень самим создавать обработчик оповещения, рефакторинг снова нам поможет. Например, напишем “ПоказатьВопрос”, и по правой кнопке выберем пункт Рефакторинг/Создать обработку оповещения. Платформа создаст обработку оповещения, и при необходимости перенесет туда код, идущий после нашего вопроса.

Что еще полезного есть в меню Рефакторинг? Любой фрагмент кода, который не содержит ключевое слово Возврат, мы можем выделить  отдельный метод. При этом платформа сама анализирует используемые переменные и предлагает либо процедуру либо функцию. Работает это просто – выделяем нужный фрагмент, и в меню Рефакторинг выбираем Выделить фрагмент.

Также мы можем переименовать метод через рефакторинг, и платформа выполнит переименование во всех модулях, где этот метод вызывается. Важно: это может потребовать значительных ресурсов, в случае, если метод используется в большом количестве мест. И платформа нас об этом предупреждает:

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

// Процедура - Выполнить обработку данных
//
// Параметры:
//  Данные		 - 	 - 
//  Параметры		 - 	 - 
//  Дополнительно	 - 	 - 
//

Нам останется только дополнить описание, прописать типы параметров, и теперь контекстная подсказка к нашему методу будет информативнее и полезнее:

// Процедура - Выполнить обработку данных
//
// Параметры:
//  Данные			 - Массив - входящие данные
//  Параметры		 - Структура - параметры обработки данных
//  Дополнительно	 - Произвольный - вспомогательные данные
//


				

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

четыре × один =

К НАЧАЛУ