Как сделать задержку при отправке писем программно в 1С

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

При реализации массовой рассылки сообщений из 1С разработчики часто сталкиваются с жесткими ограничениями почтовых провайдеров (например, лимит на отправку не более 50 писем в 15 минут). Если отправлять письма в обычном цикле без паузы, почтовый сервер заблокирует учетную запись или начнет отклонять соединения. Рассмотрим подробнее, как корректно организовать задержку выполнения кода в 1С и какие архитектурные подходы позволяют обойти ограничения почтовых служб.

Почему не стоит использовать "простой" цикл ожидания

Разберем распространенную ошибку, которую часто допускают начинающие разработчики: создание паузы с помощью циклической проверки времени.


// Неправильный подход!
ДатаНачала = ТекущаяДата();
Пока ТекущаяДата() < ДатаНачала + 30 Цикл
    // Пустой цикл ожидания
КонецЦикла;

Проанализируем ситуацию: данный код заставляет процессор выполнять бессмысленные операции на 100% мощности в течение всех 30 секунд. Это приводит к запредельной нагрузке на сервер 1С, зависанию пользовательского сеанса и замедлению работы всех остальных пользователей системы. Пауза должна быть бездеятельной, чтобы освобождать ресурсы сервера.

Способ 1. Использование фоновых заданий и метода ОжидатьЗавершения

Один из наиболее чистых способов сделать паузу на сервере без посторонних COM-объектов — запуск фонового задания с последующим ожиданием его завершения через таймаут.

Рассмотрим пример реализации:


Процедура ОтправитьПисьмаСЗадержкой()
    
    // В цикле по письмам
    Для Каждого СтрокаПисьма Из ТаблицаПисем Цикл
        
        Попытка
            РаботаСПочтовымиСообщениями.ОтправитьПочтовоеСообщение(Объект.УчетнаяЗапись, СтрокаПисьма.Параметры);
        Исключение
            ЗаписьЖурналаРегистрации("Отправка почты", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
        КонецПопытки;
        
        // Организация задержки в 30 секунд
        СделатьПаузуЧерезФоновоеЗадание(30);
        
    КонецЦикла;
    
КонецПроцедуры

Процедура СделатьПаузуЧерезФоновоеЗадание(Секунды)
    
    ПараметрыЗадания = Новый Массив;
    // Запускаем фоновое задание с несуществующим или пустым действием
    Задание = ФоновыеЗадания.Выполнить("МодульПочтовойРассылки.ПустаяПроцедура", ПараметрыЗадания);
    
    Попытка
        // Метод приостанавливает поток на указанное время
        Задание.ОжидатьЗавершения(Секунды);
    Исключение
        // Исключение по истечении таймаута является штатным поведением
    КонецПопытки;
    
КонецПроцедуры

В данном случае метод ОжидатьЗавершения приостанавливает текущий поток без избыточной нагрузки на центральный процессор.

Способ 2. Задержка через COM-объект WScript.Shell (Windows)

Если сервер 1С или клиентское приложение работают под управлением операционной системы Windows, можно воспользоваться вызовом утилиты sleep через скриптовый объект.

Посмотрим на пример кода:


Процедура СделатьПаузуWScript(Секунды)
    
    #Если Клиент Или СерверНаWindows Тогда
        Попытка
            scr = Новый COMОбъект("WScript.Shell");
            // Метод Run с параметром ожидания (1) блокирует поток на указанное время
            scr.Run("timeout /t " + СокрЛП(Число(Секунды)) + " /nobreak", 0, 1);
        Исключение
            ЗаписьЖурналаРегистрации("Пауза", УровеньЖурналаРегистрации.Предупреждение,,, ОписаниеОшибки());
        КонецПопытки;
    #КонецЕсли

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

Важное ограничение: данный метод неприменим, если сервер 1С работает под управлением ОС Linux, так как COM-объекты там недоступны.

Способ 3. Кроссплатформенная пауза через HTTP-соединение

Универсальный способ сделать паузу без нагрузки на процессор, работающий как на Windows, так и на Linux, — попытка подключения к несуществующему локальному адресу с заданным таймаутом.


Процедура ВыполнитьПаузуHTTP(Секунды)
    
    Попытка
        // Подключаемся к локальной петле с заведомо неактивным портом
        Соединение = Новый HTTPСоединение("127.0.0.1", 65534, , , , Секунды);
        Соединение.Получить(Новый HTTPЗапрос("/"));
    Исключение
        // Перехватываем исключение, которое произойдет ровно через 'Секунды'
    КонецПопытки;
    
КонецПроцедуры

Способ 4. Встроенный метод платформы ВызватьПаузу (начиная с 8.3.25)

Если ваша конфигурация работает на платформе версии 8.3.25 и выше, разработчики 1С добавили штатный механизм приостановки выполнения кода. Метод ВызватьПаузу доступен на сервере, в фоновых заданиях и сервисах.


// Интервал передается в миллисекундах (30 000 мс = 30 секунд)
ВызватьПаузу(30000);

Архитектурное решение: Очередь сообщений и регламентные задания

Использование явных пауз длительностью 30 секунд в интерактивном режиме — не самое оптимальное решение. Если необходимо отправить 100 писем, пользовательский сеанс будет заблокирован почти на час.

Правильный архитектурный подход заключается в разделении процесса на два этапа:

  1. Формирование очереди: Внешняя обработка или документ создают записи в специальном регистре сведений «Очередь сообщений» со статусом «К отправке».
  2. Отправка по расписанию: Создается регламентное задание, которое запускается раз в 5–15 минут, выбирает из регистра ограниченный лимит писем (например, не более 10–15 штук) и отправляет их за один сеанс.

Выясним преимущества такого подхода:

← На главную