Вызов процедур и функций во встроенном языке 1С 8.3: от простой строки до серверного вызова

· 9 мин

Содержание

Функцию в 1С вызывают по имени со скобками: Результат = ПосчитатьСкидку(Сумма, 5);. Если функция лежит в другом модуле, перед именем ставят имя модуля через точку: ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Значение). Чтобы такой вызов сработал, в объявлении функции должно стоять слово Экспорт.

Дальше вопрос в деталях: из какого контекста вы вызываете (клиент или сервер), как передаются параметры и что делать, когда имя функции известно только во время работы программы. Всё это общее для платформы 8.3 и не зависит от конфигурации: правила одинаковы в БП 3.0, ЗУП 3.1, УТ 11 и в собственной разработке.

Синтаксис вызова функции в 1С: процедура, функция, точка

Во встроенном языке два вида методов:

  • 📦 Процедура — выполняет действия и ничего не возвращает.
  • 🔁 Функция — выполняет действия и возвращает значение через Возврат.
Функция ПосчитатьСкидку(Сумма, Процент)
    Возврат Окр(Сумма * Процент / 100, 2);
КонецФункции

Процедура ПоказатьСкидку()
    Скидка = ПосчитатьСкидку(1200, 5); // 60
    Сообщить("Скидка: " + Формат(Скидка, "ЧДЦ=2"));
КонецПроцедуры

Функцию можно вызвать и как процедуру, отдельной строкой: ПосчитатьСкидку(1200, 5);. Ошибки не будет, возвращённое значение просто пропадёт. Обратное не работает : присвоить результат процедуры переменной нельзя.

Скобки обязательны даже без параметров: ОбновитьИтоги();. Без скобок платформа воспримет имя как переменную.

Точечная нотация

Через точку вызывают методы объектов и модулей:

Что вызываемПримерГде описан метод
Метод объекта платформыМассив.Добавить(10)встроенный тип
Экспортный метод общего модуляРаботаСФайлами.ДвоичныеДанныеФайла(Ссылка)общий модуль
Экспортный метод модуля менеджераСправочники.Номенклатура.ЗаполнитьПоУмолчанию()модуль менеджера справочника
Экспортный метод модуля объектаДокументОбъект.ЗаполнитьТовары()модуль объекта документа
Метод формы из её модуляОбновитьОтображение()модуль формы, без точки

Метод модуля объекта вызывают у объекта, а не у ссылки. Ссылка.ЗаполнитьТовары() даст ошибку, нужен Ссылка.ПолучитьОбъект().ЗаполнитьТовары().

Параметры функции в 1С: по ссылке, по значению и необязательные

По умолчанию параметр передаётся по ссылке. Функция получает ту же переменную, что лежит у вызывающего кода, и если присвоит ей новое значение, оно изменится и снаружи.

Процедура Удвоить(Число)
    Число = Число * 2;
КонецПроцедуры

А = 10;
Удвоить(А);
// А = 20

Ключевое слово Знач передаёт копию значения:

Процедура Удвоить(Знач Число)
    Число = Число * 2; // меняется только локальная копия
КонецПроцедуры

А = 10;
Удвоить(А);
// А = 10

Ловушка со ссылочными типами

Знач защищает переменную, но не содержимое объекта. Если передать массив или структуру с Знач, функция не сможет заменить сам массив, но спокойно добавит в него элементы:

Процедура Дополнить(Знач Список)
    Список.Добавить("новый");     // изменит исходный массив
    Список = Новый Массив;        // на вызывающий код не повлияет
КонецПроцедуры

Нужна независимая копия коллекции — делайте её явно: Новый ФиксированныйМассив(Массив) для защиты от изменений или копирование в цикле. В БСП есть ОбщегоНазначения.СкопироватьРекурсивно().

Значения по умолчанию и пропуск параметров

Параметру можно задать значение по умолчанию. Тогда при вызове его можно не передавать:

Функция Цена(Номенклатура, Дата = Неопределено, ВидЦены = Неопределено)
    Если Дата = Неопределено Тогда
        Дата = ТекущаяДатаСеанса();
    КонецЕсли;
    // ...
КонецФункции

Цена(Товар);                    // Дата и ВидЦены по умолчанию
Цена(Товар, , ВидЦеныОпт);      // пропущен средний параметр

Пропуск задают пустым местом между запятыми. Необязательные параметры ставьте в конец списка: так вызовы читаются проще.

Вызов функции из другого модуля: Экспорт и общие модули

Чтобы функцию видели снаружи модуля, в её объявлении ставят Экспорт:

// Общий модуль ЦеныСервер
Функция ЦенаНоменклатуры(Номенклатура, Дата) Экспорт
    // ...
КонецФункции

Вызов из любого места, где доступен модуль:

Цена = ЦеныСервер.ЦенаНоменклатуры(Товар, ТекущаяДатаСеанса());

Флажки общего модуля решают, где он доступен

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

Флажки модуляОткуда вызываетсяТипичное использование
Сервертолько с серверазапросы, запись объектов
Сервер + Вызов серверас клиента, с переходом на серверполучить данные для формы
Клиент (управляемое приложение)только с клиентадиалоги, оповещения
Клиент + Серверв обоих контекстахрасчёты без обращения к базе
Глобальныйбез имени модуляредко, утяжеляет запуск

Функцию модуля с флажком Глобальный вызывают без префикса: ЦенаНоменклатуры(...). Злоупотреблять этим не стоит: глобальные модули компилируются при старте сеанса, а имена начинают пересекаться.

Модули менеджера и объекта

Экспортная функция модуля менеджера вызывается через коллекцию: Документы.РеализацияТоваровУслуг.МояФункция(). Модуль менеджера работает на сервере, с клиента напрямую его не вызвать: нужна серверная процедура формы или общий модуль с флажком Вызов сервера.

Модуль задан строкой

Если имя модуля известно только в момент выполнения, в конфигурациях на БСП есть ОбщегоНазначения.ОбщийМодуль("ИмяМодуля"). Функция возвращает сам модуль, у которого дальше вызывают метод через точку. Это безопаснее, чем собирать вызов строкой.

Клиент и сервер: директивы компиляции и вызов из формы

В модуле управляемой формы каждая процедура помечена директивой. Она определяет, где выполняется код:

  • 🖥️ &НаКлиенте — на компьютере пользователя, доступны диалоги.
  • 🗄️ &НаСервере — на сервере, вместе с данными формы.
  • ⚡ &НаСервереБезКонтекста — на сервере, но без передачи формы.
  • 🔀 &НаКлиентеНаСервереБезКонтекста — в обоих местах, без доступа к данным базы.

Типичный вызов из кнопки:

&НаКлиенте
Процедура Пересчитать(Команда)
    Итог = ПолучитьИтогНаСервере(Объект.Контрагент);
    Сообщить(Итог);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьИтогНаСервере(Знач Контрагент)
    Запрос = Новый Запрос("ВЫБРАТЬ ... ГДЕ Контрагент = &Контрагент");
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
    // ...
    Возврат 0;
КонецФункции

С клиента на сервер можно уйти, с сервера на клиент — нет. Серверная функция не может вызвать &НаКлиенте, показать вопрос или открыть форму.

Вызов &НаСервере передаёт на сервер и обратно все данные формы, включая табличные части. Если серверной функции нужна только ссылка на контрагента, используйте &НаСервереБезКонтекста и передайте ссылку параметром.

При вызове сервера с клиента параметры, переданные по ссылке, после выполнения отправляются обратно на клиент. Знач у серверной функции убирает этот обратный путь и экономит трафик.

Динамический вызов, рекурсия и асинхронность в 1С

Выполнить и Вычислить

Когда имя функции хранится в строке, есть два оператора:

  • Выполнить(Строка) — выполняет фрагмент кода как набор операторов.
  • Вычислить(Строка) — вычисляет выражение и возвращает значение.
ИмяМетода = "ЦеныСервер.ЦенаНоменклатуры";
Цена = Вычислить(ИмяМетода + "(Товар, ДатаЦены)");

Никогда не подставляйте в Выполнить строку, которую ввёл пользователь или прислала внешняя система. Это прямой путь к выполнению чужого кода. Для внешних алгоритмов включайте УстановитьБезопасныйРежим(Истина), а в БСП используйте ОбщегоНазначения.ВыполнитьМетодКонфигурации(ИмяМетода, Параметры): он проверяет имя перед вызовом.

Код внутри строки не проверяется при сохранении модуля. Опечатка всплывёт только при выполнении, у конкретного пользователя.

Рекурсия

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

Процедура ОбойтиГруппу(Родитель, Результат)
    Выборка = Справочники.Номенклатура.Выбрать(Родитель);
    Пока Выборка.Следующий() Цикл
        Результат.Добавить(Выборка.Ссылка);
        Если Выборка.ЭтоГруппа Тогда
            ОбойтиГруппу(Выборка.Ссылка, Результат);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Рекурсии нужно условие выхода. Без него глубина вызовов растёт, пока платформа не прервёт выполнение с ошибкой переполнения стека. Для иерархии справочника часто удобнее один запрос с условием В ИЕРАРХИИ, чем рекурсивный обход.

Лямбды: чего в 1С нет и чем заменить

Анонимных функций и лямбда-выражений во встроенном языке нет. Функцию нельзя положить в переменную как значение. Замены такие:

  1. ОписаниеОповещения — объект, который хранит имя процедуры, модуль и дополнительные параметры. Его передают в немодальные диалоги, и платформа вызывает процедуру по завершении.
  2. Имя метода строкой + ОбщийМодуль() или ВыполнитьМетодКонфигурации().
  3. Общий модуль-«стратегия» — несколько модулей с одинаковым набором экспортных функций, нужный выбирается по условию.
&НаКлиенте
Процедура Удалить(Команда)
    Оповещение = Новый ОписаниеОповещения("УдалитьЗавершение", ЭтотОбъект);
    ПоказатьВопрос(Оповещение, "Удалить строку?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

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

Процедура-обработчик оповещения должна быть экспортной, иначе платформа её не найдёт.

Асинх и Ждать

С платформы 8.3.18 есть асинхронный синтаксис. Функцию помечают Асинх, а результат асинхронного метода ожидают через Ждать:

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

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

Обработчики событий: функции, которые вызывает платформа

Часть процедур вы не вызываете сами. Их вызывает платформа, когда наступает событие. Имя и параметры у таких процедур фиксированы:

СобытиеГдеКогда вызывается
ПередЗаписью(Отказ)модуль объектаперед записью в базу
ОбработкаПроведения(Отказ, Режим)модуль документапри проведении
ОбработкаЗаполнения(ДанныеЗаполнения, ...)модуль объектапри создании и вводе на основании
ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)модуль формыпри открытии формы
ПриОткрытии(Отказ)модуль формына клиенте после создания

Остановить действие можно через параметр Отказ: Отказ = Истина; в ПередЗаписью отменит запись. Это и есть пример параметра по ссылке, через который процедура возвращает результат вызывающему.

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

Подписки на события

Чтобы повесить свою функцию на запись многих объектов, не трогая их модули, используют подписку на события в ветке Общие → Подписки на события. В подписке указывают источники, событие и обработчик — экспортную процедуру общего модуля. Так типовой код остаётся без изменений, что упрощает обновление.

Как ускорить вызов функций в 1С

Сам вызов функции стоит дёшево. Время уходит на то, что происходит внутри и между клиентом и сервером.

  • 🔄 Объединяйте серверные вызовы. Три вызова сервера подряд из одной кнопки — три сетевых обращения. Соберите всё в одну серверную функцию, возвращающую структуру.
  • ⚡ &НаСервереБезКонтекста вместо &НаСервере, если форма на сервере не нужна.
  • 💾 Кэш повторного использования. У общего модуля в свойстве Повторное использование возвращаемых значений ставят На время сеанса или На время вызова. Функция с одинаковыми параметрами второй раз возьмёт результат из кэша. Подходит для настроек и констант, не подходит для меняющихся данных.
  • 🗂️ Запрос вместо цикла с обращениями через точку. Товар.Родитель.Наименование в цикле читает объекты из базы. Один запрос по всем строкам быстрее.
  • 🧹 Не вызывайте Вычислить в цикле. Строку разбирают при каждом выполнении. Определите метод один раз до цикла.

Найти медленную функцию помогает замер производительности в отладчике: в конфигураторе при отладке он включается из меню Отладка. Отчёт покажет строки и вызовы, которые съели больше всего времени.

Ошибки при вызове функции и что они означают

Диагностика: выберите симптом

Ещё одна тихая проблема: функция отрабатывает без ошибки, но значение не возвращает. Обычно в одной из ветвей Если нет Возврат. Тогда функция возвращает Неопределено, и ошибка вылезает дальше по коду.

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

Можно ли вызвать функцию из внешней обработки?
Да. Экспортные функции модуля объекта внешней обработки вызывают у объекта: Обработка = ВнешниеОбработки.Создать(Путь); Обработка.МояФункция().
Чем Выполнить отличается от Вычислить?
Выполнить исполняет операторы и ничего не возвращает. Вычислить вычисляет одно выражение и возвращает его значение.
Можно ли вернуть из функции несколько значений?
Напрямую нет, только одно. Верните структуру или массив либо используйте параметры по ссылке.
Как вызвать серверную функцию из общего клиентского модуля?
Через общий модуль с флажками «Сервер» и «Вызов сервера». Напрямую серверный модуль без этого флажка с клиента недоступен.
Можно ли из общего модуля вызвать процедуру формы?
Напрямую нет. Передайте форму параметром и вызовите у неё экспортную клиентскую процедуру: Форма.ОбновитьОтображение().