С развитием платформы «1С:Предприятие 8» и ростом популярности веб-клиента разработчики всё чаще сталкиваются с необходимостью полного отказа от синхронных вызовов. Современные браузеры (такие как Google Chrome, Mozilla Firefox, Safari) планомерно блокируют синхронные операции в основном потоке, так как они приводят к полному «зависанию» интерфейса. Работа с файловой системой, внешними компонентами, торговым оборудованием и диалоговыми окнами в веб-клиенте теперь возможна исключительно в асинхронном режиме.
В этой статье мы подробно разберем, как уйти от устаревших синхронных функций и процедур, переписав их под современные стандарты платформы. Мы проанализируем как классический подход с использованием обратных вызовов (коллбэков), так и современный элегантный механизм Асинх/Ждать.
При синхронном вызове выполнение кода останавливается на строке вызова метода и ждет, пока метод полностью завершит свою работу и вернет значение. В этот момент весь интерфейс программы заблокирован.
При асинхронном вызове выполнение основного потока кода продолжается сразу же после инициации процесса. Сам процесс выполняется параллельно (или в фоновом режиме браузера), а результат возвращается позже через специальную процедуру-обработчик или возобновление выполнения приостановленной функции. Это гарантирует, что пользовательский интерфейс приложения остается отзывчивым.
Давайте разберем по шагам, какими способами можно осуществить переход на асинхронную модель разработки.
Это наиболее удобный и лаконичный способ, который появился в платформе «1С:Предприятие 8» для упрощения рефакторинга. Он позволяет писать асинхронный код «линейно», сохраняя привычную структуру синхронных функций. Для этого используются ключевые слова Асинх (указывается перед объявлением метода) и Ждать (указывается перед вызовом асинхронной функции).
Рассмотрим практический пример. Предположим, у нас был синхронный код получения каталога документов и последующей работы с файлами:
&НаКлиенте
Процедура ВыполнитьИмпортСинхронно()
// Синхронный вызов, который заблокирует веб-клиент
Каталог = КаталогДокументов();
// Дальнейшая работа с каталогом
Сообщить("Выбран путь: " + Каталог);
ИмпортироватьФайлыИзКаталога(Каталог);
КонецПроцедуры
Давайте перепишем этот код с использованием конструкции Асинх/Ждать. Выясним, что для этого нужно изменить:
Асинх перед объявлением процедуры.КаталогДокументов() на его асинхронный аналог КаталогДокументовАсинх().Ждать.Посмотрим на результат рефакторинга:
&НаКлиенте
Асинх Процедура ВыполнитьИмпортАсинхронно()
// Платформа приостановит выполнение этой процедуры, но интерфейс не зависнет
Каталог = Ждать КаталогДокументовАсинх();
// Код продолжит выполнение здесь, когда пользователь выберет каталог или система вернет результат
Сообщить("Выбран путь: " + Каталог);
ИмпортироватьФайлыИзКаталога(Каталог);
КонецПроцедуры
Этот подход невероятно удобен, так как сохраняет локальный контекст переменных и позволяет использовать привычные конструкции обработки ошибок.
Приятной особенностью конструкции Асинх/Ждать является то, что вы можете продолжать обрабатывать исключительные ситуации стандартным блоком Попытка — Исключение — КонецПопытки. Проанализируем пример обработки ошибок при чтении файла:
&НаКлиенте
Асинх Процедура ПрочитатьФайлАсинхронно(ПутьКФайлу)
Попытка
ТекстФайла = Ждать ПолучитьТекстИзФайлаАсинх(ПутьКФайлу);
ОбработатьТекст(ТекстФайла);
Исключение
ПоказатьПредупреждение(, "Не удалось прочитать файл: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Если ваша конфигурация должна поддерживать работу на старых версиях платформы (ниже 8.3.18), или по какой-то причине вы не можете использовать синтаксический сахар Асинх/Ждать, вам придется использовать классический механизм обратных вызовов (callback-функций). Он базируется на создании объекта ОписаниеОповещения и вызове методов, начинающихся с приставки Начать....
Давайте разберем по шагам, как переписать наш пример с получением каталога документов через обратные вызовы:
ОписаниеОповещения, указывая имя процедуры, которая примет результат работы.НачатьПолучениеКаталогаДокументов) и передаем туда наше оповещение.Рассмотрим, как это выглядит в коде 1С:
&НаКлиенте
Процедура ВыполнитьИмпортЧерезОповещение()
// Создаем описание оповещения, связывая его с процедурой-обработчиком
Оповещение = Новый ОписаниеОповещения("ВыполнитьИмпортЗавершение", ЭтотОбъект);
// Инициируем асинхронный процесс. Код процедуры сразу завершится, освобождая интерфейс
НачатьПолучениеКаталогаДокументов(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьИмпортЗавершение(Каталог, ДополнительныеПараметры) Экспорт
// Сюда управление передается платформой автоматически, когда каталог будет получен
Если Каталог <> Неопределено Тогда
Сообщить("Выбран путь: " + Каталог);
ИмпортироватьФайлыИзКаталога(Каталог);
КонецЕсли;
КонецПроцедуры
Важное требование: Процедура-обработчик завершения обязательно должна иметь свойство Экспорт, иначе платформа не сможет вызвать ее в контексте формы.
При использовании классических обратных вызовов локальные переменные исходной процедуры будут утеряны, так как она полностью завершает свою работу еще до того, как сработает оповещение. Для передачи нужного контекста (например, идентификаторов документов, ссылок или признаков) используется параметр ДополнительныеПараметры объекта ОписаниеОповещения.
Рассмотрим пример передачи структуры контекста:
&НаКлиенте
Процедура НачатьЗагрузкуФайлаСКонтекстом(ИдентификаторСтроки, ИмяДокумента)
// Упаковываем все необходимые данные в структуру
КонтекстВызова = Новый Структура;
КонтекстВызова.Вставить("ИдентификаторСтроки", ИдентификаторСтроки);
КонтекстВызова.Вставить("ИмяДокумента", ИмяДокумента);
Оповещение = Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтотОбъект, КонтекстВызова);
// Вызываем асинхронный метод
НачатьПомещениеФайла(Оповещение, "", "Выбрать файл для загрузки", ИнтерактивныйРежимВыбораФайла.Выбрать);
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(Результат, ДополнительныеПараметры) Экспорт
// Получаем переданный ранее контекст из ДополнительныеПараметры
СтрокаТаблицы = ДополнительныеПараметры.ИдентификаторСтроки;
ДокументСсылка = ДополнительныеПараметры.ИмяДокумента;
Если Результат <> Неопределено Тогда
// Обрабатываем результат загрузки файла с учетом контекста
ОбработатьИмпортированныйФайл(Результат, СтрокаТаблицы, ДокументСсылка);
КонецЕсли;
КонецПроцедуры
Чтобы быстро найти в своей конфигурации все участки кода, требующие рефакторинга, воспользуйтесь стандартными инструментами Конфигуратора 1С.
Проанализируем шаги для автоматического поиска таких мест:
Также не забудьте настроить свойства самой конфигурации для предотвращения случайного написания устаревшего кода в будущем. Откройте свойства корневого элемента конфигурации и установите следующие параметры в значение Не использовать:
Эти настройки принудительно заблокируют запуск синхронных процедур при отладке и тестировании, что позволит вам вовремя обнаружить слабые места конфигурации еще до того, как они попадут к конечным пользователям веб-клиента.