Я совсем начинающий программист, но уже пару раз приходилось искать как вызвать паузу в 1С и чего я только не видел: внешние компоненты, powershell, ping .... Ну, главное, чего от них требовалось, они делали - пауза срабатывала! С выходом платформы 8.3.25 я обрадовался, несмотря на общее разочарование, переписал паузу на такой набор функций:
Процедура Пауза2_0(Секунд, НужнаТочность = Ложь) Экспорт
Интервал = Секунд * 1000;
Если НужнаТочность Тогда
Интервал = Интервал - 110; // примерно столько добавляет вызов задания на моей "тачке"
КонецЕсли;
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(Интервал);
ФЗ = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.НуВызватьПаузу", ПараметрыЗадания);
ФЗ.ОжидатьЗавершенияВыполнения();
КонецПроцедуры
Процедура НуВызватьПаузу(Длительность) Экспорт
ВызватьПаузу(Длительность); //Хотели, чтобы мы вызывали это только в фоновых заданиях - пожалуйста!
КонецПроцедуры
А потом мне понадобилось залезть в дебри надстройки Битрикс (я на обычных формах работаю, здесь не расширение) и я заметил интересную функцию Таймаут. Мне стало интересно, как разработчики Битрикс реализовали паузу. Я посмотрел и вижу вызов в фоновом задании функции "ОбщегоНазначенияБТС.Пауза". Постойте, так реализация паузы уже есть в БСП? Ну-ка, ну-ка:
Процедура Пауза(Секунд) Экспорт
ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание(); //если вызвать из фонового задания, возвращает ссылку на него
Если ФоновоеЗадание = Неопределено Тогда // мы еще не в фоновом задании, делаем небольшую рекурсию
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ОбщегоНазначенияБТС.Пауза", Параметры);
КонецЕсли;
Попытка
ФоновоеЗадание.ОжидатьЗавершения(Секунд); //Тут совершается магия
Исключение
Возврат;
КонецПопытки;
КонецПроцедуры
Так, непонятно, как так получается, что двойной вызов ОжидатьЗавершения (или рекомендуемой теперь ОжидатьЗавершенияВыполнения) приводит к паузе ровно на время ожидания? А оказывается просто: ОжидатьЗавершенияВыполнения, вызванный внутри фонового задания приводит к банальной паузе по таймауту, и следующий код в фоновом задании также приведет к паузе:
ЗаписьЖурналаРегистрации("Доработки. Вай мама", УровеньЖурналаРегистрации.Информация, , , "1");
ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание();
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
ЗаписьЖурналаРегистрации("Доработки. Вай мама", УровеньЖурналаРегистрации.Информация, , , "2");
Приведет к записям в журнале:
Выходит, пауза, которую нам так радостно презентовали уже давно была что ли?
Вопрос. Зачем они это сделали? Они просто сказать не могли что ли, или они сами не ожидали, что ОжидатьЗавершенияВыполнения так заработает и решили подстраховаться, вдруг баг, ставший фичей, прекратит свое существование?
В общем, я переделал их паузу в связи с обновлением платформы, благо режим совместимости здесь не учитывается и теперь радуюсь, что у меня один метод вместо двух прежних и ничего не выскакивает (!):
Процедура Пауза(Секунд) Экспорт
ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание();
Если ФоновоеЗадание = Неопределено Тогда
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.Пауза", Параметры);
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
Иначе
Интервал = Цел(Секунд * 1000);
Если Интервал > 0 Тогда
ВызватьПаузу(Интервал);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
P.S. Нееет, такую тему как пауза так просто оставлять нельзя, поколения разработчиков кустарных методов мне этого не простят, да и мне странно. ПОдумаешь, пауза! В общем, я решил некоим образом почтить память изысканных велосипедостроителей, подаривших мне и другим самые разные способы паузы в 1С и придумать свои, естественно, не выходящие уже за рамки стандартного кода 1С и исключительно ее посредством (для кросс-платформенности, например). Естественно, в меру своих способностей, так что уж прошу не обессудьте!
1. Первая пауза наводит порядок в методах БСП. А там проблема - используется нерекомендуемый метод! Устраняем и при этом сохраняем совместимость со старыми платформами (ведь не все перешли на 8.3.25)
Процедура ПаузаЛегаси(Секунд) Экспорт
ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание();
Если ФоновоеЗадание = Неопределено Тогда
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.ПаузаЛегаси", Параметры);
КонецЕсли;
//Уходим от использования нерекомендуемых методов
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
КонецПроцедуры
2. Вторая, заставляет призадуматься на тему, а что если пауза в нашем приложении такая же частая гостья, как Обработка проведения или хотя бы СохранитьЗначение? Даешь паузу для Всех! (Размещать в модуле без отметки "Клиент"!)
Процедура ПаузаОптовик(Секунд) Экспорт
// Здравствуйте, уважаемый, у Вас курить есть?
УстановитьПривилегированныйРежим(Истина);
ЗаданияКурить = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Состояние, Наименование", СостояниеФоновогоЗадания.Активно, "Курить"));
Если ЗаданияКурить.Количество() > 0 Тогда
// отгрузите мне немного
ЗаданияКурить[0].ОжидатьЗавершенияВыполнения(Секунд);
Иначе
//запрашиваю новую поставку!
ФоновоеЗадание = ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание();
Если ФоновоеЗадание <> Неопределено И Найти(ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание().ИмяМетода, "ПаузаОптовик") Тогда
//вы уже запрашивали, поставка сейчас НЕ-ВОЗ-МО-ЖНА, подождите (было, что забыл указать наименование задания, 3000 заданий - не предел! Да и мало ли)
ВызватьПаузу(Секунд * 1000);
Возврат;
Иначе
Параметры = Новый Массив;
Параметры.Добавить(10800);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмыСервер.ПаузаОптовик", Параметры, , "Курить");
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
КонецЕсли;
КонецЕсли;
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Естественно, размещать нужно в модуле без отметки "Клиент", иначе Привилегированный Режим превратится в тыкву
Процедура ПаузаОптовикЛегаси(Секунд) Экспорт
Отметка = ТекущаяУниверсальнаяДата();
// Здравствуйте, уважаемый, у Вас курить есть?
УстановитьПривилегированныйРежим(Истина);
ЗаданияКурить = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Состояние, Наименование", СостояниеФоновогоЗадания.Активно, "Курить"));
Если ЗаданияКурить.Количество() > 0 Тогда
// отгрузите мне немного
ЗаданияКурить[0].ОжидатьЗавершенияВыполнения(Секунд);
Секунд = Секунд - (ТекущаяУниверсальнаяДата() - Отметка);
КонецЕсли;
Если Секунд > 0 Тогда
//запрашиваю новую поставку!
Параметры = Новый Массив;
Параметры.Добавить(10800);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмыСервер.ПаузаОптовикЛегаси", Параметры, , "Курить");
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
КонецЕсли;
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
При этом, что любопытно, ограничение на ожидание фонового задания только своего пользователя или любого при наличии административных прав легко обходятся получением привилегированного режима, поэтому не важно, под каким пользователем запущена пауза, все смогут ей пользоваться!
3. Следующий метод использует ОжидатьЗавершенияВыполнения на полную катушку. Ведь если задуматься, у нас на рабочих базах постоянно выполняются какие-то фоновые задания. А что если их тормознуть и стрельнуть парочку секунд (без ущерба для них, разумеется, мы ж не звери какие):
Процедура ПаузаХулиганская(Секунд) Экспорт
ОтметкаВремени = ТекущаяУниверсальнаяДатаВМиллисекундах();
Попытка
Если Цел(Секунд * 1000) > 0 Тогда
//Курить есть?
ВызватьПаузу(Цел(Секунд * 1000));
КонецЕсли;
Исключение
// а если найду?
УстановитьПривилегированныйРежим(Истина);
СостоятельныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Состояние", СостояниеФоновогоЗадания.Активно));
Индекс = СостоятельныеЗадания.Количество();
СколькоЕщеКурить = Секунд * 1000 - ТекущаяУниверсальнаяДатаВМиллисекундах() + ОтметкаВремени;
Пока СколькоЕщеКурить > 0 Цикл //так, а у Вас что?
Индекс = Индекс - 1;
Если Индекс < 0 Тогда
// курим свои, чего уж там
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.ПаузаХулиганская", Параметры);
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(СколькоЕщеКурить / 1000);
Иначе
// ага, а это что?
СостоятельныеЗадания[Индекс].ОжидатьЗавершенияВыполнения(СколькоЕщеКурить / 1000);
КонецЕсли;
СколькоЕщеКурить = Секунд * 1000 - ТекущаяУниверсальнаяДатаВМиллисекундах() + ОтметкаВремени;
КонецЦикла;
УстановитьПривилегированныйРежим(Ложь);
КонецПопытки;
КонецПроцедуры
Лично для себя я сделал вывод, что планирование ночных заданий можно снабдить новым инструментом: планирование последовательности выполнения заданий без необходимости сращивать их жестко, как и "угадывать", когда закончится предыдущее задание - можно просто задать в меру своей фантазии ключи или наименования к заданиям, которые будут регулировать очередность и приоритет их выполнения - здорово! Также, обратите внимание: новый метод паузы, в отличие от старого не умеет обрабатывать ни отрицательных чисел, ни чисел с дробной частью. Просто выпадает в ошибку, поэтому даже в таком раздолбайском методе приходится добавлять такие скучные проверки (очкариков не бьем, беременных не спрашиваем)
4. А, вы еще здесь? Тогда добавим в копилку паузу, использующую новый метод и основанный на доверии:
Процедура ПаузаПравославная(Секунд, УжеВФоновомЗадании = Ложь) Экспорт
Если УжеВФоновомЗадании = Ложь Тогда
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
Параметры.Добавить(Истина);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.ПаузаПравославная", Параметры);
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(Секунд);
Иначе
ВызватьПаузу(Цел(Секунд * 1000));
КонецЕсли;
КонецПроцедуры
Все просто - в фоновом запускаем вызов паузы, в клиент-серверном режиме (так пишет сама 1С в сообщении об ошибке) - вызываем фоновым себя же.
5. Ну и напоследок, странный метод, который старается выжать максимум точности (но у него получается хуже, чем у одного из вышестоящих методов, угадайте какого):
Процедура ПаузаСионисткая(Секунд) Экспорт
//Говорят, Моисей 40 лет водил евреев по пустыне, прежде чем они обрели свою родину, сколько у вас бродят фоновые задания, прежде чем начать заняться делом, тоже неизвестно
ОтметкаВремени = ТекущаяУниверсальнаяДатаВМиллисекундах();
ТекущийСеансИнформационнойБазы = ПолучитьТекущийСеансИнформационнойБазы();
ФоновоеЗадание = ТекущийСеансИнформационнойБазы.ПолучитьФоновоеЗадание();
Если ФоновоеЗадание = Неопределено Тогда
ДанныеОтклонения = ХранилищеОбщихНастроек.Загрузить("СреднееОтклонениеПаузы");
Параметры = Новый Массив;
Параметры.Добавить(Секунд);
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ДополнительныеМеханизмы.ПаузаСионисткая", Параметры);
НачальноеВремяОжидания = ?(ДанныеОтклонения = Неопределено, Секунд, Секунд - ДанныеОтклонения.ВремяОтклонения / ДанныеОтклонения.Количество / 500);
НачальноеВремяОжидания = НачальноеВремяОжидания * 1000;
ВремяОжидания = НачальноеВремяОжидания;
ОтметкаВремени = ТекущаяУниверсальнаяДатаВМиллисекундах();
Пока ВремяОжидания > 10 Цикл
ФоновоеЗадание.ОжидатьЗавершенияВыполнения(0.005);
ВремяОжидания = НачальноеВремяОжидания - ТекущаяУниверсальнаяДатаВМиллисекундах() + ОтметкаВремени;
КонецЦикла;
СохранитьДанныеОтклонения(ОтметкаВремени, НачальноеВремяОжидания);
Иначе
Интервал = Цел(Секунд * 1000);
Если Интервал > 0 Тогда
ВызватьПаузу(Интервал);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Здесь для каждого пользователя ведется подробный учет того, сколько раз была запущена пауза и как долго ее пришлось ждать по сравнению с желаемым. Чтобы постараться выровнять время ожидания около точного времени, заданного параметром, вычитаем двойную меру среднего отклонения. А в остальном, обычная пауза, кто придумал, что это связано с евреями?
Ну и напоследок, думаю стоит показать Вам результаты моих замеров, ведь нельзя было сравнивать методы только по внешнему виду. Это не экзамен, конечно, но и чай, не на скамейке в парке на красивых девушек глазеем. Хочется отметить, что основная неточность вызова паузы в отдельном задании в том, что завершается оно почему-то не так быстро, как запускается, отсюда взялись мои 110 миллисекунд "для точности". Однако, здесь нам на выручку приходит с куда большей точностью работающий таймер отслеживания завершения - ОжидатьЗавершенияВыполнения (он к тому же и не такой требовательный к входным данным, корректно переваривает все, только буквы не подавал). Серьезных замеров не делал, чтобы меня не обвинили в поклонении паузе (упаси боже!), но не одну сотню раз по одной секунде у меня каждый метод успел отработать. И вот результат (в миллисекундах):
Зачем-то еще делал паузу на отдельной форме для того, чтобы блокировать только окно владельца, позволяя спокойно работать в это время с остальным интерфейсом, но кому я об этом рассказываю, никто же на 1С шахмат и морского боя не делает. Пока! Чао! До свиданья
P.P.S. По советам трудящихся сравнил замеры и для метода ожидания интернет-ресурса, естественно, недоступного.
Есть особенность этого метода, которую нужно учитывать - значения таймаута запроса округляются по методу Цел, соответственно, попытка подождать 0,5 секунд приведет к паузе в 60 секунд (значение по умолчанию). Будьте внимательны!
С другой стороны, этот метод не требует вызова сервера - для УФ несомненный плюс!
Процедура ПаузаОтрицания(Секунд) Экспорт
Соединение = Новый HTTPСоединение("127.0.0.0", , , , , Секунд);
Попытка
Соединение.Получить(Новый HTTPЗапрос());
Исключение
КонецПопытки;
КонецПроцедуры
Как ни странно, точность у такого подхода на высоте:
Причем в данном случае также захотелось сравнить и нагрузку на процессор во время паузы, вдруг требование ресурса отнимает у процессора драгоценное время, но нет и здесь все в порядке на время всех 8 методов (примерно по 2 минуты каждый):
Отдельные всплески не считаем, средняя нагрузка на бюджетный процессор под пользовательской ОС в пределах 2%. Особо плоское плато показывают Хулиганская, Оптовик и ... ПаузаОтрицания - факт! Но, конечно, ничего не мешает нам с умом пользоваться любой из пауз, если они, конечно, у вас есть!