Пауза и ожидание во встроенном языке 1С 8.3
Содержание
Готового оператора Sleep, как в других языках, во встроенном языке 1С нет. Паузу собирают из того, что есть: на клиенте в управляемой форме это ПодключитьОбработчикОжидания, на сервере — цикл по таймеру с проверкой ОбработкаПрерыванияПользователя, а для долгого ожидания — фоновое задание и метод ОжидатьЗавершенияВыполнения.
Выбор метода зависит не от длительности паузы, а от того, где выполняется код. Клиентская и серверная пауза ведут себя по-разному: одна освобождает интерфейс, вторая держит занятым рабочий процесс кластера. Ниже — как сделать каждую и чем они обходятся.
Короткое правило: на клиенте — обработчик ожидания, на сервере — по возможности не паузу, а отложенный запуск через задание.
Чтобы не держать все способы в голове, вот они рядом:
| Способ | Где работает | Блокирует поток | Когда применять |
|---|---|---|---|
| Цикл по таймеру | Сервер и толстый клиент | Да | Пауза в доли секунды |
ПодключитьОбработчикОжидания | Управляемая форма (клиент) | Нет | Отложенный или повторяющийся вызов |
ОжидатьЗавершенияВыполнения | Сервер | Да, до завершения задания | Дождаться фоновой операции |
| Регламентное задание | Сервер | Нет | Запуск по расписанию |
| Таймаут соединения | Сервер и клиент | Нет (ограничивает ожидание) | Обращение к внешнему сервису |
Пауза в серверном коде: цикл по таймеру
На сервере приостановить выполнение можно только одним надёжным способом — крутить пустой цикл, сверяясь с таймером. Текущее время в миллисекундах даёт функция ТекущаяУниверсальнаяДатаВМиллисекундах():
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
ПаузаМс = 3000; // 3 секунды
Пока (ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) < ПаузаМс Цикл
ОбработкаПрерыванияПользователя();
КонецЦикла;
Вызов ОбработкаПрерыванияПользователя() внутри цикла обязателен: без него пользователь не сможет оборвать операцию, а длинный цикл платформа может счесть зависанием.
Этот цикл занимает рабочий процесс на всё время паузы. Пока он крутится, ядро процессора загружено, сеанс жив, а если пауза внутри транзакции — держатся блокировки. Для паузы в доли секунды это терпимо, для нескольких секунд — уже нет.
Для измерения интервала берите именно ТекущаяУниверсальнаяДатаВМиллисекундах(), а не ТекущаяДата(). Первая возвращает число миллисекунд и не зависит от часового пояса сеанса, вторая даёт точность только до секунды — для коротких пауз этого мало, а при переводе часов результат может «скакнуть».
Иногда советуют вызывать Sleep из Windows через COM-объект. Способ работает только на Windows, непредсказуем в клиент-серверном варианте и на сервере его лучше не применять. Если пауза нужна именно на сервере и она дольше пары секунд — это сигнал, что задачу пора решать заданием, а не паузой.
Пауза на клиенте без заморозки формы
В управляемой форме пустой цикл недопустим — он подвесит интерфейс. Вместо паузы здесь работает таймер: процедура ПодключитьОбработчикОжидания регистрирует метод, который платформа вызовет сама через заданный интервал.
&НаКлиенте
Процедура ПриОткрытииНаСервере(...)
// ...
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьСтатус", 5);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСтатус()
// код выполняется каждые 5 секунд
КонецПроцедуры
Форма при этом остаётся живой: пользователь работает, а обработчик срабатывает в паузах между действиями.
Интервал задаётся в секундах. Для целого значения минимум — 1 секунда; дробный интервал (например, 0,5) поддерживается в актуальных версиях платформы, минимально допустимое дробное значение — порядка 0,2 секунды. Задать паузу в 50 миллисекунд через обработчик нельзя.
Пара важных ограничений:
- Подключить обработчик можно только из клиентского контекста управляемого приложения. Из серверной процедуры вызвать
ПодключитьОбработчикОжиданияне получится — паузу на сервере так не сделать. - На одно имя процедуры действует один обработчик. Повторный вызов
ПодключитьОбработчикОжиданияс тем же именем переопределяет интервал, а не добавляет второй таймер. - Интервал — это пауза между вызовами, а не жёсткий период. Если предыдущий вызов ещё выполняется или форма занята, следующее срабатывание сместится.
Разовый таймер и его отключение
По умолчанию обработчик ожидания повторяется бесконечно. Чтобы он сработал один раз — отложенный запуск после открытия формы, — третьим параметром передают Истина:
// сработает ровно один раз через 1 секунду
ПодключитьОбработчикОжидания("ПоказатьПодсказку", 1, Истина);
Повторяющийся обработчик нужно отключать явно, иначе он продолжит вызываться:
ОтключитьОбработчикОжидания("ОбновитьСтатус");
Типичный сценарий с отключением — ждать завершения фоновой операции и прекратить опрос, как только она закончилась:
- При открытии формы подключить обработчик с интервалом 2–3 секунды.
- В обработчике проверить состояние задания на сервере.
- Когда задание завершилось — вызвать
ОтключитьОбработчикОжиданияи показать результат.
Имя процедуры в ОтключитьОбработчикОжидания должно совпадать с тем, что передали при подключении, иначе таймер останется активным и продолжит дёргать сервер.
В современной платформе (8.3.18 и новее) есть асинхронные методы с ключевыми словами Асинх и Ждать — они позволяют не блокировать интерфейс при вызове диалогов и длительных операций. Но отдельной асинхронной «паузы» среди них нет: для отложенного по времени вызова в форме по-прежнему используют обработчик ожидания. Асинхронные методы решают задачу «не заморозить форму на время ожидания ответа», а не «подождать N секунд».
Прерывание долгого цикла пользователем
Отдельная задача — не пауза, а возможность остановить затянувшийся расчёт. За это отвечает ОбработкаПрерыванияПользователя(). Метод проверяет, не нажал ли пользователь прерывание (на толстом клиенте — Ctrl+Break), и если нажал — прерывает выполнение.
Ставьте этот вызов внутри любого длинного цикла — и обработки больших выборок, и паузы по таймеру:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ОбработкаПрерыванияПользователя();
// обработка строки
КонецЦикла;
Без этого вызова долгая операция выглядит как зависшая: пользователь не может её отменить, а платформа не отдаёт управление. Это дешёвая страховка — одна строка в теле цикла.
ПодключитьОбработчикОжидания — платформа сама вызовет вашу процедуру через заданный интервал, форма не зависнет.ТекущаяУниверсальнаяДатаВМиллисекундах() с вызовом ОбработкаПрерыванияПользователя() внутри. Процесс занят всё это время — только для коротких пауз.ОжидатьЗавершенияВыполнения, а не держите основной поток в цикле.Ожидание завершения фонового задания
Когда нужно запустить длительную операцию и дождаться её результата, паузу заменяют фоновым заданием. Основной поток не крутит пустой цикл, а ждёт конкретное событие — завершение задания.
Задание = ФоновыеЗадания.Выполнить(
"МойОбщийМодуль.ДлительнаяОперация", , , "Пересчёт итогов");
// ждать завершения, но не дольше 30 секунд
Задание.ОжидатьЗавершенияВыполнения(30);
Если Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
// забрать результат
КонецЕсли;
Метод ОжидатьЗавершенияВыполнения блокирует вызывающий поток до завершения задания или до истечения указанного интервала (в секундах). Если задание не успело закончиться за отведённое время — выполнение продолжится, а состояние останется «Выполняется».
Прежнее название метода — ОжидатьЗавершения — считается устаревшим; в новом коде используйте ОжидатьЗавершенияВыполнения.
Полноценный неблокирующий сценарий выглядит так. Клиент запускает фоновое задание на сервере и получает его идентификатор. Затем на клиенте подключается обработчик ожидания с интервалом 2–3 секунды. На каждом срабатывании клиент коротким серверным вызовом проверяет состояние задания по идентификатору. Как только состояние стало «Завершено» или «Отменено» — обработчик отключается и форма показывает результат. Основной поток при этом нигде не висит в цикле.
Такой подход особенно важен для операций, которые могут идти минуту и дольше: выгрузка, обмен, пересчёт регистров. Держать клиентское соединение в ожидании всё это время нельзя — оно оборвётся по таймауту, а пользователь будет смотреть на застывшую форму.
На клиенте связка «запустить фоновое задание + опрос через обработчик ожидания» предпочтительнее серверного ожидания: интерфейс остаётся отзывчивым, а сервер не держит лишний сеанс в режиме ожидания.
Отложенный запуск вместо паузы: регламентные задания
Если задача звучит как «выполнить код через 10 минут» или «каждую ночь», пауза не нужна вовсе — нужен планировщик. В платформе это регламентные задания с расписанием.
Логику оформляют регламентным заданием, а периодичность и время запуска задают объектом Расписание либо в настройках задания. Платформа сама запустит процедуру в нужный момент — без цикла, без занятого процесса между запусками.
Два важных отличия по вариантам работы:
- В клиент-серверном варианте регламентные задания выполняет сервер автоматически, по расписанию.
- В файловом варианте они отрабатывают только пока открыт сеанс, назначенный их выполнять; при закрытой программе задание не стартует.
Регламентное задание — правильная замена длинной серверной паузе: оно не занимает рабочий процесс в промежутках и переживает перезапуск сервера.
Таймауты соединений и обработка ошибок
Отдельный класс «ожиданий» — обращения наружу: HTTP-сервисы, веб-сервисы, внешние источники данных. Здесь пауза не задаётся вручную, но важно ограничить время ожидания ответа, иначе поток зависнет на недоступном адресе.
У HTTPСоединение в конструкторе есть параметр таймаута (в секундах) — задавайте его явно, не полагаясь на бесконечное ожидание. Любой сетевой вызов оборачивайте в Попытка, чтобы обрыв связи не ронял всю обработку:
Попытка
Ответ = Соединение.Получить(Запрос);
Исключение
Сообщить("Сервис недоступен: " + ОписаниеОшибки());
// обработать таймаут/ошибку соединения
КонецПопытки;
Текст исключения разбирают через ИнформацияОбОшибке() или ОписаниеОшибки(). Таймаут соединения приходит сюда же как исключение — отдельного «кода ожидания» платформа не возвращает.
Таймаут задаётся не везде одинаково:
- у
HTTPСоединение— параметром конструктора; - у веб-сервисных и WSDL-соединений — своими настройками прокси и подключения;
- у
COMОбъектуправляемого таймаута ожидания, как правило, нет — зависший внешний вызов COM прервать штатно сложно, поэтому к COM на сервере относятся осторожно.
Разумное время ожидания — несколько секунд для внутренней сети и 30–60 секунд для внешних сервисов. Бесконечное ожидание (таймаут не задан) опасно: один недоступный адрес способен подвесить рабочий процесс.
Таймаут соединения и пауза в коде — разные вещи. Таймаут ограничивает ожидание ответа извне, а не приостанавливает выполнение по вашей команде.
Как пауза влияет на производительность кластера
Любая серверная пауза — это занятый рабочий процесс rphost. Чем дольше и чаще такие паузы, тем меньше у кластера свободных ресурсов на реальную работу.
Что происходит под капотом при пустом цикле ожидания на сервере:
- 🔥 одно ядро процессора загружено вхолостую всё время паузы;
- 🔒 если пауза внутри транзакции — блокировки держатся дольше, растёт риск конфликтов и таймаутов ожидания блокировок у соседних сеансов;
- 🧮 активный сеанс занимает лицензию и слот в кластере;
- ⏳ при десятках таких пауз одновременно рабочие процессы упираются в потолок, остальные пользователи ждут.
Отсюда практические выводы:
- Серверную паузу держите в пределах долей секунды.
- Долгое ожидание переносите на клиент (обработчик ожидания) или в фоновое задание.
- Периодические действия оформляйте регламентным заданием, а не циклом с паузой.
- Никогда не ставьте паузу внутри открытой транзакции.
Пауза — почти всегда признак того, что архитектуру можно упростить: заменить ожидание событием, опросом по таймеру или заданием по расписанию.
Отдельно про отладку. Иногда паузу добавляют временно, чтобы «поймать» момент в отладчике или воспроизвести гонку по времени. Для разовой проверки это нормально, но такие вставки легко забыть в коде. Перед выкладкой в рабочую базу пройдитесь поиском по циклам ожидания и уберите отладочные паузы — в продуктивной среде они оборачиваются лишней нагрузкой и жалобами на «тормоза».
Частые вопросы
Есть ли в 1С оператор Sleep, как в других языках?
ТекущаяУниверсальнаяДатаВМиллисекундах(). Прямого оператора приостановки потока во встроенном языке не предусмотрено.Почему обработчик ожидания не вызывается чаще раза в секунду?
Зависнет ли форма на время паузы?
Как прервать зависший цикл с паузой?
ОбработкаПрерыванияПользователя(). Тогда на толстом клиенте операцию можно оборвать сочетанием Ctrl+Break. Без этого вызова прервать выполнение штатно не получится.Чем дождаться окончания долгой операции, не крутя цикл?
ОжидатьЗавершенияВыполнения с таймаутом в секундах. Метод вернёт управление, как только задание завершится или истечёт указанное время.