С развитием платформы 1С:Предприятие 8.3 разработчики столкнулись с необходимостью повсеместного отказа от модальных окон. Долгое время основным инструментом для реализации немодальности было ОписаниеОповещения. Однако этот подход приводил к так называемому «адскому колбэку» (callback hell), когда логика одной процедуры разрывалась на несколько фрагментов, что затрудняло чтение и отладку кода. Внедрение синтаксиса Асинх (Async) и Ждать (Await) кардинально изменило ситуацию, позволив писать асинхронный код так же просто, как и синхронный.
В данной статье мы подробно разберем, как работают эти механизмы, проанализируем примеры кода и выясним, почему этот подход эффективнее старых методов. Мы рассмотрим не только базовый синтаксис, но и механизмы обработки ошибок, а также работу с групповыми операциями через Обещание.Все.
Давайте вспомним, как выглядел стандартный код для вызова простого вопроса пользователю до появления нового синтаксиса. Нам приходилось создавать отдельную процедуру-обработчик и передавать её через специальный объект. Проанализируем типичную ситуацию:
&НаКлиенте
Процедура ЗаполнитьТовары(Команда)
Оповещение = Новый ОписаниеОповещения("ЗаполнитьТоварыВопросЗавершение", ЭтотОбъект);
ТекстВопроса = "Табличная часть будет очищена. Продолжить?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьТоварыВопросЗавершение(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
// Алгоритм заполнения, который оторван от основной процедуры
КонецЕсли;
КонецПроцедуры
В этой ситуации основная проблема заключается в разрыве контекста. Если внутри первой процедуры были локальные переменные, их приходилось передавать через структуру ДополнительныеПараметры, что усложняло поддержку кода. Теперь разберем, как этот же сценарий реализуется с помощью Ждать.
Новый синтаксис позволяет нам не прерывать логическую цепочку кода. Мы помечаем вызывающую процедуру модификатором Асинх, а перед вызовом асинхронного метода ставим оператор Ждать. Посмотрим на обновленный пример:
&НаКлиенте
Асинх Процедура ЗаполнитьТовары(Команда)
ТекстВопроса = "Табличная часть будет очищена. Продолжить?";
Результат = Ждать ПоказатьВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНет);
Если Результат = КодВозвратаДиалога.Да Тогда
// Алгоритм заполнения продолжается прямо здесь!
ОчиститьТабличнуюЧасть();
ЗаполнитьДанные();
КонецЕсли;
КонецПроцедуры
Обратите внимание на ключевые изменения:
Асинх.ПоказатьВопросАсинх вместо ПоказатьВопрос).Когда мы вызываем функцию с пометкой Асинх, она не возвращает результат немедленно. Вместо этого она возвращает объект специального типа — Обещание (аналог Promise в JavaScript). Если мы используем оператор Ждать, платформа приостанавливает выполнение текущего кода, сохраняет все значения локальных переменных (контекст) и возвращает управление интерфейсу. Пользователь может продолжать работу с другими элементами формы.
Как только асинхронная операция (например, ответ пользователя в диалоге) завершается, «Обещание» переходит в состояние «Выполнено», и платформа восстанавливает выполнение процедуры с того места, где мы остановились. Этот механизм называется конечным автоматом.
Иногда нам требуется запустить несколько задач параллельно и дождаться завершения всех сразу. В современных релизах платформы (начиная с 8.3.19) для этого предусмотрен метод Обещание.Все(). Рассмотрим ситуацию, когда нам нужно асинхронно проверить наличие нескольких файлов на диске:
&НаКлиенте
Асинх Процедура ПроверитьФайлы(Команда)
МассивОбещаний = Новый Массив;
МассивОбещаний.Добавить(НайтиФайлыАсинх("C:\Temp\file1.txt"));
МассивОбещаний.Добавить(НайтиФайлыАсинх("C:\Temp\file2.txt"));
// Ждем завершения всех операций сразу
Результаты = Ждать Обещание.Все(МассивОбещаний);
Для Каждого Файлы Из Результаты Цикл
Если Файлы.Количество() > 0 Тогда
Сообщить("Файл найден: " + Файлы[0].Имя);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Использование Обещание.Все значительно ускоряет работу, если операции независимы друг от друга, так как они могут выполняться платформой более эффективно, чем строго последовательный вызов через Ждать для каждой строки.
Одним из важнейших преимуществ нового подхода является возможность использования стандартных блоков Попытка — Исключение. В случае с ОписаниеОповещения перехват ошибок внутри обратного вызова был затруднен. Теперь же мы можем обернуть вызов в привычную конструкцию:
&НаКлиенте
Асинх Процедура КопироватьДанные(Команда)
Попытка
Ждать КопироватьФайлыАсинх("C:\Source", "D:\Destination");
Сообщить("Копирование успешно завершено");
Исключение
Сообщить("Произошла ошибка при копировании: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Важный момент: Если асинхронная функция внутри себя генерирует исключение, это исключение «пробрасывается» в то место, где стоит оператор Ждать. Это позволяет централизованно обрабатывать ошибки бизнес-логики.
При работе с механизмами Асинх/Ждать следует помнить о нескольких правилах, которые мы проанализировали в ходе обсуждения:
А через Ждать внутри функции Б, то функция Б также должна быть помечена как Асинх.Вопрос() -> ПоказатьВопросАсинх(), НайтиФайлы() -> НайтиФайлыАсинх().Ждать, пользователь может успеть закрыть форму или нажать другую кнопку. Всегда проверяйте актуальность данных (например, ЭтаФорма.Открыта()) после выхода из состояния ожидания.Механизм Асинх — Ждать — это мощный инструмент, который делает код на языке 1С чище и понятнее. Мы выяснили, что он не только заменяет неудобные ОписанияОповещения, но и предоставляет полноценные инструменты для работы с групповыми операциями и обработки ошибок. Переход на этот стандарт обязателен для современных конфигураций, работающих в веб-клиенте или мобильных приложениях, где блокирующие вызовы интерфейса недопустимы.