Как работает асинхронный оператор Ждать в платформе 1С

· 9 мин

Содержание

Оператор Ждать в платформе 1С запускает асинхронный код так, будто он синхронный: строка Результат = Ждать ВопросАсинх(...) дожидается ответа пользователя, но интерфейс при этом не замирает. Работает он только внутри процедуры или функции, помеченной словом Асинх, и только на клиенте — в тонком, веб- и мобильном клиенте.

Код на обработчиках оповещения (ОписаниеОповещения) быстро превращается в лапшу из вложенных процедур: показали вопрос — ответ пришёл в другую процедуру, там новый вопрос — снова отдельный обработчик. Ждать убирает эту вложенность: вся логика остаётся в одном методе, сверху вниз.

Ниже — синтаксис, тип Обещание, таймауты диалогов, почему не блокируется поток интерфейса и чем Ждать отличается от обработчика ожидания и фоновых заданий. С последними двумя его путают чаще всего.

Для чего нужен оператор и в какой версии он появился

Ждать, ключевое слово Асинх и тип Обещание появились в версии платформы 8.3.18. Если база работает на платформе с номером ниже 8.3.18, этих средств нет — там доступны только старые модели.

Исторически в 1С сложились три способа работать с операциями, которые не завершаются мгновенно (диалог с пользователем, выбор файла, обращение к серверу):

  • 🧱 Синхронные методы — Вопрос(), Предупреждение(), ВвестиЧисло(). Останавливают выполнение и замораживают интерфейс до ответа. В веб-клиенте по умолчанию запрещены, считаются устаревшими.
  • 🔁 Обработчики оповещения — ПоказатьВопрос() и подобные методы с объектом ОписаниеОповещения. Результат приходит в отдельную процедуру. Работают везде, но код дробится на куски.
  • ⚡ Асинхронные методы с Ждать — методы с суффиксом Асинх (ВопросАсинх, ПредупреждениеАсинх) и ваши собственные асинхронные функции. Пишутся линейно, не морозят интерфейс.

Чтобы разница была наглядной, сравните два подхода к одной задаче — спросить подтверждение и, если согласен, выполнить действие. На обработчике оповещения код разрывается на две процедуры:

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

&НаКлиенте
Процедура ПослеВопроса(Результат, Параметры) Экспорт
    Если Результат = КодВозвратаДиалога.Да Тогда
        УдалитьСтрокиНаСервере();
    КонецЕсли;
КонецПроцедуры

С Ждать то же самое умещается в один метод без «прыжка» в отдельную процедуру. Чем больше шагов в сценарии, тем заметнее выигрыш: три вопроса подряд на обработчиках оповещения превращаются в три связанные процедуры, а на Ждать — в три строки одной процедуры.

Ждать — это современная замена первым двум подходам. Новый код имеет смысл писать именно на нём.

Синтаксис: слово Асинх, оператор Ждать и тип Обещание

Процедура или функция, внутри которой вы хотите применить Ждать, обязана начинаться со слова Асинх. Без него компилятор выдаст ошибку ещё при проверке модуля.

&НаКлиенте
Асинх Процедура ПодтвердитьУдаление()

    Ответ = Ждать ВопросАсинх(
        "Удалить выделенные строки?",
        РежимДиалогаВопрос.ДаНет);

    Если Ответ = КодВозвратаДиалога.Да Тогда
        УдалитьСтрокиНаСервере();
    КонецЕсли;

КонецПроцедуры

Как это читается: ВопросАсинх не возвращает ответ сразу — он возвращает Обещание (тип Обещание, по-английски Promise). Оператор Ждать берёт это обещание, приостанавливает процедуру до момента, когда пользователь нажмёт кнопку, и подставляет в переменную Ответ уже готовый результат — значение КодВозвратаДиалога.

Пока процедура «ждёт», она не держит поток интерфейса — платформа возвращает управление в цикл обработки событий. Пользователь может двигать окно, сам диалог отрисовывается нормально.

Любая Асинх-функция снаружи возвращает Обещание, даже если внутри вы пишете обычный Возврат ЗначениеФункции. Поэтому вызывать такую функцию тоже нужно через Ждать:

Асинх Функция ПользовательСогласен()
    Ответ = Ждать ВопросАсинх("Продолжить?", РежимДиалогаВопрос.ДаНет);
    Возврат (Ответ = КодВозвратаДиалога.Да);
КонецФункции

Асинх Процедура ЗапуститьОбработку()
    Если Ждать ПользовательСогласен() Тогда
        // ...
    КонецЕсли;
КонецПроцедуры

Если применить Ждать к обычному значению, а не к обещанию, оператор просто вернёт это значение. Ошибки не будет, но и смысла в таком вызове нет.

Чего можно дождаться: ответы, файлы и свои функции

«Ожидание событий» в 1С — это ожидание завершения асинхронной операции. Через Ждать удобно дожидаться:

  • ответов пользователя в диалогах — ВопросАсинх, ПредупреждениеАсинх, методы ввода значений с суффиксом Асинх;
  • операций с файлами на стороне клиента (выбор, помещение файла) — у них тоже есть асинхронные варианты;
  • результата ваших собственных Асинх-функций, которые внутри обращаются к серверу.

Важный момент про «синхронизацию потоков»: встроенный язык 1С на клиенте выполняется в одном потоке. Параллельных нитей, которые нужно синхронизировать мьютексами, здесь нет. Ждать не распараллеливает код — он кооперативно приостанавливает текущий метод и отдаёт управление платформе, пока операция не закончится. Это ближе к очереди событий, чем к многопоточности.

Несколько ожиданий подряд выстраиваются в линейный сценарий без вложенных обработчиков:

Асинх Процедура Мастер()

    Имя = Ждать ВвестиСтрокуАсинх("Введите наименование");
    Если Имя = Неопределено Тогда
        Возврат; // пользователь отменил ввод
    КонецЕсли;

    Подтверждение = Ждать ВопросАсинх(
        "Сохранить «" + Имя + "»?",
        РежимДиалогаВопрос.ДаНет);

    Если Подтверждение = КодВозвратаДиалога.Да Тогда
        СохранитьНаСервере(Имя);
    КонецЕсли;

КонецПроцедуры

Отдельный частый сценарий — своя Асинх-функция, которая обращается к серверу и возвращает результат клиенту. Внутри неё серверный вызов остаётся обычным, а наружу функция отдаёт Обещание:

&НаКлиенте
Асинх Функция ЗагрузитьОстатки(Склад)
    Данные = ОстаткиСервер(Склад); // вызов серверной функции
    Возврат Данные;
КонецФункции

&НаКлиенте
Асинх Процедура ОбновитьТаблицу()
    Остатки = Ждать ЗагрузитьОстатки(Склад);
    ЗаполнитьТаблицуЗначениями(Остатки);
КонецПроцедуры

Если асинхронная операция завершится ошибкой, исключение возникнет прямо на строке с Ждать. Его можно перехватить привычной конструкцией Попытка … Исключение … КонецПопытки — так же, как для обычного кода:

Попытка
    Остатки = Ждать ЗагрузитьОстатки(Склад);
Исключение
    ПоказатьПредупреждение(, "Не удалось получить остатки: "
        + ОписаниеОшибки());
    Возврат;
КонецПопытки;

Это удобнее разрозненных обработчиков ошибок: весь сценарий и его обработка исключений находятся рядом, в одном методе.

Таймауты диалогов и прерывание ожидания

У диалоговых методов есть параметр таймаута — время в секундах, через которое окно закроется само. Значение 0 означает «ждать без ограничения».

Ответ = Ждать ВопросАсинх(
    "Продолжить операцию?",
    РежимДиалогаВопрос.ДаНет,
    30); // диалог закроется через 30 секунд

По истечении таймаута диалог закрывается автоматически, и возвращённое значение может оказаться Неопределено (пользователь ничего не выбрал). Всегда проверяйте результат перед тем, как его использовать, иначе получите ошибку сравнения или обращения к неопределённому значению.

Отдельного способа «прервать» уже запущенный Ждать в языке нет: операция либо завершается, либо падает с исключением. Если нужна именно отменяемая длительная операция (расчёт на сервере, который пользователь может остановить), это решается не оператором Ждать, а механизмом длительных операций: сервер запускает фоновое задание, клиент периодически опрашивает его состояние и может задание отменить. Сам таймаут диалога к серверному расчёту отношения не имеет — он управляет только временем жизни окна.

Почему интерфейс не зависает и при чём тут фоновые задания

Разница между синхронным и асинхронным вызовом видна сразу. Синхронный Вопрос() останавливает весь клиент: пока открыт диалог, форма не перерисовывается, таймеры не срабатывают. Асинхронный Ждать ВопросАсинх(...) возвращает управление платформе, и интерфейс остаётся живым.

Из-за похожего слова «ждать/фоновый» эти механизмы постоянно смешивают. Разведём их:

Что этоГде выполняетсяДля чего
Ждать / Асинхклиент, один потокдождаться диалога, файла, серверного вызова без заморозки формы
Фоновое заданиесервер, отдельный процесстяжёлый расчёт или длительная обработка данных

Ждать не выносит вычисления в фон и не ускоряет серверный код. Если внутри Асинх-функции вы вызываете долгую серверную процедуру напрямую, сервер всё равно будет считать столько, сколько нужно, — просто клиент в это время не повиснет наглухо.

Чтобы тяжёлую работу действительно вынести в фон, её запускают фоновым заданием на сервере (в типовых конфигурациях на базе библиотеки стандартных подсистем для этого есть готовый механизм длительных операций), а на клиенте дожидаются результата. Клиентскую обёртку над опросом фонового задания удобно оформить как Асинх-функцию и вызвать её через Ждать — тогда из кода формы это выглядит как одна строка.

Отзывчивый интерфейс — это и риск: пока идёт ожидание, пользователь может нажать ту же кнопку второй раз и запустить операцию повторно. Если действие не должно дублироваться, в начале Асинх-метода ставьте флаг «операция идёт» и блокируйте кнопку, а по завершении снимайте флаг. С синхронным вызовом такой защиты не требовалось, потому что интерфейс был заморожен.

Чем Ждать отличается от обработчика ожидания и других способов

Не каждую задержку решают через Ждать. Есть ещё ПодключитьОбработчикОжидания — таймер формы, который периодически вызывает вашу процедуру. Это разные инструменты.

// запустить опрос каждые 3 секунды
ПодключитьОбработчикОжидания("ПроверитьСостояние", 3);

// остановить, когда больше не нужно
ОтключитьОбработчикОжидания("ПроверитьСостояние");

Процедура-обработчик должна быть клиентской и без параметров. Третий параметр ПодключитьОбработчикОжидания со значением Истина делает срабатывание однократным — удобно для отложенного действия «через N секунд».

Сводка по способам:

СпособБлокирует интерфейсСтиль кодаКогда применять
Синхронные методы (Вопрос)Далинейныйтолько в старом коде; в вебе запрещены
ОписаниеОповещенияНетвложенные процедурыподдержка старого кода
Ждать / АсинхНетлинейныйновый клиентский код с диалогами и вызовами сервера
ПодключитьОбработчикОжиданияНетпо таймерупериодический опрос, отложенный запуск
Что выбрать для вашей задачи?

Типичные ошибки и отладка

Большинство проблем с Ждать — это несколько повторяющихся ситуаций.

  • Ждать вне Асинх-метода. Компилятор не пропустит модуль. Пометьте процедуру словом Асинх или уберите Ждать.
  • Забыли Ждать перед вызовом. Тогда в переменную попадёт сам объект Обещание, а не результат. Сравнение с КодВозвратаДиалога.Да всегда даст ложь, а причину не видно. Проверьте, что перед асинхронным вызовом стоит Ждать.
  • Попытка применить на сервере. Асинхронная модель — клиентская. В серверном методе Ждать не используется; серверный код выполняется синхронно.
  • Смешение с модальными синхронными вызовами. Нельзя из Асинх-метода вызывать старый модальный Вопрос() — переведите вызов на ВопросАсинх.

При отладке точки останова на строках с Ждать работают штатно. Отладчик проходит по коду сверху вниз, как в синхронном методе, — в этом и ценность линейного стиля. Учитывайте, что в момент Ждать управление уходит платформе: пока открыт диалог, могут отработать другие обработчики формы. Если логика на это не рассчитана, возможны неожиданные пересечения — проверяйте состояние формы после каждого Ждать, а не считайте его «замороженным».

Частые вопросы

С какой версии платформы доступен оператор Ждать?
С версии 8.3.18. На платформе с номером ниже оператора Ждать, слова Асинх и типа Обещание нет — там работают только обработчики оповещения и синхронные методы.
Можно ли использовать Ждать в серверном модуле?
Нет. Асинхронная модель предназначена для клиента (тонкого, веб- и мобильного). Серверный код выполняется синхронно, слово Асинх к серверным методам не применяют.
Чем Обещание отличается от результата функции?
Обещание — это объект-«расписка», что результат появится позже. Любая Асинх-функция возвращает именно обещание. Чтобы получить само значение, его разворачивают оператором Ждать.
Замораживает ли Ждать интерфейс, как синхронный Вопрос?
Нет. На время ожидания управление возвращается платформе, форма остаётся отзывчивой. Это главное отличие от синхронного Вопрос(), который держит клиент до ответа.
Как поймать ошибку асинхронной операции?
Оберните строку с Ждать в Попытка … Исключение … КонецПопытки. Если операция завершится неудачно, исключение возникнет на строке с Ждать, и его можно обработать как обычно.
Работает ли Ждать в веб-клиенте и на мобильном клиенте?
Да. Асинхронная модель поддерживается во всех клиентских вариантах с платформы 8.3.18. Более того, в веб-клиенте это предпочтительный способ: старые модальные синхронные диалоги там по умолчанию недоступны.
Можно ли вызвать Асинх-функцию без Ждать?
Технически да — тогда вы получите объект Обещание, а не результат. Это осмысленно, только если обещание нужно сохранить и развернуть через Ждать позже. В большинстве случаев отсутствие Ждать перед вызовом — ошибка.