1C-Code → Основы 1С → Клиент и сервер: директивы компиляции
Где выполняется каждая строка и почему от этого зависит скорость. Пять директив, разница между ними и цена лишнего вызова.
Глава 22 из 36 · чтение примерно 26 минут
В 1С код выполняется в двух разных местах, и это не подробность реализации, а то, о чём разработчик думает постоянно.
Клиент — программа на компьютере человека. Видит экран, умеет спросить и показать. К базе доступа не имеет вовсе.
Сервер — там, где данные. Видит базу, выполняет запросы, проводит документы. Человека не видит: ни спросить, ни показать не может.
Между ними сеть. Каждый переход — это упакованные данные, отправленные по проводу и распакованные обратно.
| Директива | Где выполняется | Видит данные формы |
|---|---|---|
| &НаКлиенте | у человека | да |
| &НаСервере | на сервере | да — их передают туда и обратно |
| &НаСервереБезКонтекста | на сервере | нет |
| &НаКлиентеНаСервереБезКонтекста | и там, и там | нет |
| &НаКлиентеНаСервере | и там, и там (общий модуль) | нет |
Что такое «контекст формы»
Контекст — это все данные формы: реквизиты, элементы, сам объект. Директива &НаСервере означает не только «выполнить на сервере», но и «взять с собой всю форму»: платформа упакует её, отправит на сервер и вернёт обратно.
На форме документа с тремя сотнями строк это ощутимый объём на каждый вызов. Поэтому &НаСервереБезКонтекста почти всегда быстрее: он ничего не везёт, работает с тем, что передали параметрами.
Правило простое: если функции не нужна форма целиком — она без контекста.
Один переход клиент → сервер стоит немного. Пятьдесят переходов подряд стоят заметно, а на медленном канале — мучительно.
Самая частая ошибка выглядит так: цикл по строкам табличной части, а внутри вызов серверной функции «получить цену». Сто строк — сто походов по сети.
Лечится одинаково всегда: собрать данные, сходить один раз, разложить результат. Вместо ста вызовов по одному товару — один вызов со списком из ста товаров.
&НаСервереБезКонтекста
Функция ЦеныПоТоварам(Товары)
// один запрос по списку
КонецФункции
&НаКлиенте
Процедура Заполнить()
Цены = ЦеныПоТоварам(СписокТоваров);
КонецПроцедуры
| Часть | Что означает |
|---|---|
&НаСервереБезКонтекста | форму с собой не везём — нам нужен только список |
Функция ЦеныПоТоварам(Товары) | принимает СПИСОК, а не один товар |
&НаКлиенте | здесь мы рядом с человеком и его экраном |
Цены = ЦеныПоТоварам(…) | ровно один переход по сети вместо ста |
На чём спотыкаются
Ставят &НаСервере, когда хватает без контекста. Работает, но возит форму туда и обратно на каждый вызов.
Обращаются к базе из &НаКлиенте. Справочники.Номенклатура на клиенте недоступны — ошибка «переменная не определена» там, где имя очевидно существует.
Пытаются спросить человека с сервера. Вопрос() и Предупреждение() — клиентские. С сервера пользователя нет: надо вернуть результат на клиент и спрашивать там.
Сообщают с сервера и ждут, что человек увидит сразу. Сообщения копятся и доедут к концу вызова, а не в момент выполнения строки.
Если непонятно, где выполняется код, — посмотрите, что ему доступно. Видит Элементы и Объект — клиент или сервер с контекстом. Видит Справочники и Запрос — сервер. Видит и то и другое — сервер с контекстом.
Замер производительности в конфигураторе показывает число серверных вызовов. Обычно именно оно, а не сложность кода, объясняет «форма открывается пять секунд».
Директивы живут в модуле формы. У общего модуля вместо них галочки в свойствах, и означают они то же самое: где этот код вообще существует.
| Флаг | Что означает | Когда ставят |
|---|---|---|
| Сервер | код есть на сервере | почти всегда — это основной флаг |
| Клиент (управляемое приложение) | код есть на клиенте | работа с формами, диалогами, файлами |
| Вызов сервера | клиент может вызвать серверную функцию этого модуля | без него клиент модуль не увидит, даже если модуль серверный |
| Внешнее соединение | код доступен при подключении извне | обмены и интеграции |
| Привилегированный | код выполняется без проверки прав | служебные операции — ставят редко и осознанно |
| Повторное использование | результат функции запоминается | справочные значения, которые спрашивают много раз |
«Метод не обнаружен» у серверного модуля
Частая ошибка первой недели: написали функцию в общем модуле, зовут с клиента — «метод объекта не обнаружен».
Причина почти всегда одна: у модуля стоит «Сервер», но не стоит «Вызов сервера». Без этого флага клиент про модуль просто не знает: с его стороны такого имени нет.
Второй вариант той же ошибки — функция без Экспорт. Тогда её не видит вообще никто снаружи модуля.
Повторное использование: кэш на один вызов
Флаг «Повторное использование возвращаемых значений» заставляет платформу запоминать результат функции для тех же аргументов — на время вызова или на время сеанса.
Это правильный ответ на «функция ходит в базу за одним и тем же двести раз». Но у него есть цена: результат устаревает. Функция, вернувшая курс валюты, будет отдавать его и после того, как курс поменяли.
Поэтому так делают для того, что меняется редко: настройки, служебные ссылки, описания метаданных. Для остатков и цен — никогда.
Это ломает голову всем, кто пришёл из обычных форм. В управляемом приложении Вопрос и Предупреждение не возвращают ответ: код не ждёт человека, а идёт дальше.
Ответ приходит в отдельную процедуру — оповещение. Её имя передают в ПоказатьВопрос, и продолжение кода пишут уже в ней.
Причина в веб-клиенте: браузер не умеет остановить выполнение и ждать. Поэтому и в тонком клиенте всё сделано одинаково — асинхронно.
✓ Так — как в обычных формах
Ответ = Вопрос("Записать?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
Записать();
КонецЕсли;В управляемом приложении так нельзя: в веб-клиенте это просто не работает, а код идёт дальше, не дождавшись человека.
✕ Так — с оповещением
Опис = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект);
ПоказатьВопрос(Опис, "Записать?", РежимДиалогаВопрос.ДаНет);
&НаКлиенте
Процедура ПослеВопроса(Ответ, Параметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
Записать();
КонецЕсли;
КонецПроцедурыПродолжение кода живёт в отдельной процедуре. Выглядит длиннее — зато работает и в тонком клиенте, и в браузере.
Асинхронное всё, не только вопросы
По тому же правилу устроены выбор файла (НачатьПомещениеФайла), открытие формы с ожиданием результата (ОткрытьФорму с оповещением), выбор даты и цвета.
Узнать их легко по приставке: Показать…, Начать… — асинхронные, ответ придёт в оповещение. В новых версиях есть и Асинх-функции с Ждать, которые пишутся короче, но смысл тот же: код не останавливается.
Иногда коду нужно сделать то, на что у пользователя прав нет: записать служебный регистр, прочитать настройку, до которой его роль не допускает.
Для этого есть УстановитьПривилегированныйРежим(Истина) и модули с флагом «Привилегированный». Внутри такого режима права не проверяются вовсе.
Правило обращения простое: включать на минимальный кусок и выключать сразу. Привилегированный режим, включённый «на всякий случай» в начале большой процедуры, — это дыра, через которую пользователь увидит чужие данные и не узнает об этом.
Упражнение 2. Где выполнять этот код
Напишите Куда(Задача): по описанию работы вернуть директиву. «запрос» — &НаСервереБезКонтекста; «элементы» — &НаСервере (нужен контекст формы); «вопрос» — &НаКлиенте. Проверка спрашивает про «запрос».
Решается в тренажёре: код запускается и проверяется сразу.
«Вызов сервера» — это не мгновенный переход, а упаковка данных, отправка, распаковка на той стороне и то же самое обратно.
При &НаСервере едет весь контекст формы: реквизиты, табличные части, состояние элементов. На форме документа с тысячей строк это заметно, и заметно дважды — туда и обратно.
При &НаСервереБезКонтекста едут только параметры и результат. Поэтому такие вызовы и предпочитают: не из-за чистоты кода, а из-за объёма.
И третье, о чём забывают: не всё вообще можно отправить. Таблица значений несериализуема — вернуть её с сервера на клиент нельзя, про это была глава «Таблица значений».
Долгая операция в вызове сервера
Пока серверный вызов не закончился, форма у человека стоит. Обработка на пять минут внутри &НаСервере — это пять минут замершего окна и ощущение, что программа зависла.
Для долгого есть фоновые задания: запустить, вернуть управление форме, а результат забрать по готовности. В БСП для этого есть готовый механизм длительных операций.
Признак, что пора туда: если операция может занять больше нескольких секунд на реальных данных — а не на ваших трёх тестовых строках.
Упражнение 1. Сколько вызовов сервера
Функция ВызововСервера(Строк, ПоОдной) считает, сколько переходов на сервер потребуется. Если ПоОдной истина — по вызову на каждую строку. Если ложь — один вызов на все. Строк ноль — вызовов ноль в обоих случаях.
Решается в тренажёре: код запускается и проверяется сразу.
Клиент видит человека и не видит базу; сервер наоборот. Директива говорит, где выполняется процедура и везти ли с собой форму. Без контекста — быстрее, и выбирают его, если форма целиком не нужна. Вызов в цикле — главная причина медленных форм: собрать, сходить один раз, разложить. Спрашивать человека можно только с клиента.
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.
← Модули: какой когда выполняется
→ Формы: реквизиты, элементы, события