В процессе разработки на платформе 1С:Предприятие часто возникают ситуации, когда необходимо приостановить выполнение программного кода на определенное время. Это может потребоваться при интеграции с внешними системами (например, ожидание ответа от API, у которого есть лимиты на количество запросов в секунду), при работе с торговым оборудованием или при ожидании завершения фонового процесса. Рассмотрим подробнее, как правильно реализовать задержку выполнения итерации цикла, разберем достоинства и недостатки каждого метода.
ПриостановитьРаботающийПотокНачиная с версии платформы 8.3.18, разработчики получили штатный и наиболее правильный инструмент для решения этой задачи. Метод ПриостановитьРаботающийПоток() позволяет остановить выполнение текущего потока кода на заданное количество секунд. Проанализируем его преимущества: он работает кроссплатформенно (Windows/Linux), доступен как на клиенте, так и на сервере, и, что самое важное, не нагружает процессор бесполезными вычислениями.
Рассмотрим пример использования данного метода в цикле:
Для каждого СтрТЧ Из Объект.Товары Цикл
// Приостанавливаем выполнение на 5 секунд
ПриостановитьРаботающийПоток(5);
// Выполняем полезную нагрузку
ВыполнитьЗаполнениеТЧ(СтрТЧ);
КонецЦикла;
Этот метод является предпочтительным, так как он корректно взаимодействует с операционной системой, "засыпая" и освобождая ресурсы процессора для других задач. Если ваша версия платформы ниже 8.3.18, нам придется рассмотреть альтернативные варианты.
Если разработка ведется под управлением ОС Windows, мы можем воспользоваться возможностями операционной системы. Одним из классических способов является использование объекта WScript.Shell. Разберем по шагам, как это работает: мы создаем COM-объект, который имеет встроенный метод Sleep, принимающий время задержки в миллисекундах.
// 5000 миллисекунд = 5 секунд
Пауза = Новый COMОбъект("WScript.Shell");
Для каждого СтрТЧ Из Объект.Товары Цикл
Пауза.Run("ping -n 1 -w 5000 127.0.0.1", 0, Истина);
// Или более простой вариант через sleep в скрипте, но Run с ping часто надежнее
ВыполнитьЗаполнениеТЧ(СтрТЧ);
КонецЦикла;
Однако стоит помнить, что создание COM-объекта на каждой итерации — это ресурсозатратная операция. Правильнее будет инициализировать объект один раз перед циклом. Также отметим, что данный метод не будет работать в среде Linux или в облачных сервисах, где запрещен запуск внешних компонентов.
pingЕще один "хитрый" способ, упомянутый в обсуждении, — использование системной утилиты ping. Суть метода заключается в том, что мы отправляем запрос на локальный адрес и заставляем систему ждать ответа определенное время. Выясним причину популярности этого метода: он не требует регистрации дополнительных библиотек.
Для каждого СтрТЧ Из Объект.Товары Цикл
КомандаСистемы("ping -n 1 -w 5000 127.0.0.1");
ВыполнитьЗаполнениеТЧ(СтрТЧ);
КонецЦикла;
Важный нюанс: использование КомандаСистемы может приводить к появлению "черных окон" консоли, что пугает пользователей. Для скрытого выполнения лучше использовать метод ЗапуститьПриложение с соответствующими параметрами видимости окна.
ТекущаяДатаИногда разработчики прибегают к самому простому, на первый взгляд, решению — циклу, который сравнивает текущее время с моментом начала паузы. Посмотрим на пример реализации:
Процедура Пауза(Секунд)
ДатаНачала = ТекущаяДата();
Пока ТекущаяДата() - ДатаНачала < Секунд Цикл
// Процессор работает на 100%, выполняя это сравнение
КонецЦикла;
КонецПроцедуры
Внимание! Мы крайне не рекомендуем использовать этот метод. Проанализируем ситуацию: во время выполнения такого цикла одно ядро вашего процессора будет загружено на 100%. Если такой код запустится на сервере, где работают десятки пользователей, это может привести к катастрофическому падению производительности всей системы 1С. Этот метод допустим только в исключительных случаях для очень коротких задержек (менее 1 секунды) и только на стороне клиента.
ПодключитьОбработчикОжиданияЕсли задача стоит на стороне клиента (в управляемом приложении) и нам важно, чтобы интерфейс программы не "зависал" во время ожидания, следует использовать ПодключитьОбработчикОжидания. Этот метод не делает паузу внутри кода в привычном понимании, а велит платформе вызвать определенную процедуру через заданный интервал.
Разберем, как перестроить алгоритм перебора таблицы значений на асинхронный лад:
&НаКлиенте
Перем ТекущийИндекс;
&НаКлиенте
Процедура НачатьОбработкуТЧ()
ТекущийИндекс = 0;
// Запускаем обработку первой строки через 5 секунд
ПодключитьОбработчикОжидания("ОбработатьСледующуюСтроку", 5, Истина);
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьСледующуюСтроку()
Если ТекущийИндекс < Объект.Товары.Количество() Тогда
СтрТЧ = Объект.Товары[ТекущийИндекс];
ВыполнитьЗаполнениеТЧ(СтрТЧ);
ТекущийИндекс = ТекущийИндекс + 1;
// Рекурсивно подключаем обработчик для следующей строки
ПодключитьОбработчикОжидания("ОбработатьСледующуюСтроку", 5, Истина);
КонецЕсли;
КонецПроцедуры
Преимущество этого метода в том, что пользователь может продолжать работу с формой, пока 1С в фоновом режиме (для пользователя) обрабатывает строки таблицы с нужными интервалами.
Для старых версий платформы (ниже 8.3.18) на стороне сервера существует способ сделать паузу, не нагружая процессор. Мы можем инициировать HTTP-запрос к несуществующему IP-адресу с заданным таймаутом ожидания.
Процедура ПаузаНаСервере(Секунды)
НТТР = Новый HTTPСоединение("127.0.0.2", , , , , Секунды);
Попытка
НТТР.Получить(Новый HTTPЗапрос("/"));
Исключение
// Платформа подождет указанное количество секунд и вылетит по таймауту
КонецПопытки;
КонецПроцедуры
Этот метод изящнее пустого цикла, так как ожидание ответа от сокета не нагружает CPU, однако он выглядит "костыльным" и может логировать ошибки сетевых соединений в системных журналах.
Подводя итог, выделим лучшие практики:
ПриостановитьРаботающийПоток().ПодключитьОбработчикОжидания().WScript.Shell.ТекущаяДата() на сервере.Выбор конкретного метода зависит от архитектуры вашего решения и требований к стабильности системы. Надеемся, этот разбор поможет вам реализовать задержку в коде максимально эффективно!