1C-CodeОсновы 1С → Клиент и сервер: директивы компиляции

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

Где выполняется каждая строка и почему от этого зависит скорость. Пять директив, разница между ними и цена лишнего вызова.

Глава 22 из 36 · чтение примерно 26 минут

В 1С код выполняется в двух разных местах, и это не подробность реализации, а то, о чём разработчик думает постоянно.

Клиент — программа на компьютере человека. Видит экран, умеет спросить и показать. К базе доступа не имеет вовсе.

Сервер — там, где данные. Видит базу, выполняет запросы, проводит документы. Человека не видит: ни спросить, ни показать не может.

Между ними сеть. Каждый переход — это упакованные данные, отправленные по проводу и распакованные обратно.

Пять директив

ДирективаГде выполняетсяВидит данные формы
&НаКлиентеу человекада
&НаСерверена сервереда — их передают туда и обратно
&НаСервереБезКонтекстана серверенет
&НаКлиентеНаСервереБезКонтекстаи там, и тамнет
&НаКлиентеНаСервереи там, и там (общий модуль)нет

Что такое «контекст формы»

Контекст — это все данные формы: реквизиты, элементы, сам объект. Директива &НаСервере означает не только «выполнить на сервере», но и «взять с собой всю форму»: платформа упакует её, отправит на сервер и вернёт обратно.

На форме документа с тремя сотнями строк это ощутимый объём на каждый вызов. Поэтому &НаСервереБезКонтекста почти всегда быстрее: он ничего не везёт, работает с тем, что передали параметрами.

Правило простое: если функции не нужна форма целиком — она без контекста.

Цена вызова

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

Самая частая ошибка выглядит так: цикл по строкам табличной части, а внутри вызов серверной функции «получить цену». Сто строк — сто походов по сети.

Лечится одинаково всегда: собрать данные, сходить один раз, разложить результат. Вместо ста вызовов по одному товару — один вызов со списком из ста товаров.

Один вызов вместо цикла

&НаСервереБезКонтекста
Функция ЦеныПоТоварам(Товары)
	// один запрос по списку
КонецФункции

&НаКлиенте
Процедура Заполнить()
	Цены = ЦеныПоТоварам(СписокТоваров);
КонецПроцедуры
ЧастьЧто означает
&НаСервереБезКонтекстаформу с собой не везём — нам нужен только список
Функция ЦеныПоТоварам(Товары)принимает СПИСОК, а не один товар
&НаКлиентездесь мы рядом с человеком и его экраном
Цены = ЦеныПоТоварам(…)ровно один переход по сети вместо ста

На чём спотыкаются

Ставят &НаСервере, когда хватает без контекста. Работает, но возит форму туда и обратно на каждый вызов.

Обращаются к базе из &НаКлиенте. Справочники.Номенклатура на клиенте недоступны — ошибка «переменная не определена» там, где имя очевидно существует.

Пытаются спросить человека с сервера. Вопрос() и Предупреждение() — клиентские. С сервера пользователя нет: надо вернуть результат на клиент и спрашивать там.

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

Как это выглядит в отладке

Если непонятно, где выполняется код, — посмотрите, что ему доступно. Видит Элементы и Объект — клиент или сервер с контекстом. Видит Справочники и Запрос — сервер. Видит и то и другое — сервер с контекстом.

Замер производительности в конфигураторе показывает число серверных вызовов. Обычно именно оно, а не сложность кода, объясняет «форма открывается пять секунд».

Общие модули: те же правила, только шире

Директивы живут в модуле формы. У общего модуля вместо них галочки в свойствах, и означают они то же самое: где этот код вообще существует.

Флаги общего модуля

ФлагЧто означаетКогда ставят
Серверкод есть на серверепочти всегда — это основной флаг
Клиент (управляемое приложение)код есть на клиентеработа с формами, диалогами, файлами
Вызов сервераклиент может вызвать серверную функцию этого модулябез него клиент модуль не увидит, даже если модуль серверный
Внешнее соединениекод доступен при подключении извнеобмены и интеграции
Привилегированныйкод выполняется без проверки правслужебные операции — ставят редко и осознанно
Повторное использованиерезультат функции запоминаетсясправочные значения, которые спрашивают много раз

«Метод не обнаружен» у серверного модуля

Частая ошибка первой недели: написали функцию в общем модуле, зовут с клиента — «метод объекта не обнаружен».

Причина почти всегда одна: у модуля стоит «Сервер», но не стоит «Вызов сервера». Без этого флага клиент про модуль просто не знает: с его стороны такого имени нет.

Второй вариант той же ошибки — функция без Экспорт. Тогда её не видит вообще никто снаружи модуля.

Повторное использование: кэш на один вызов

Флаг «Повторное использование возвращаемых значений» заставляет платформу запоминать результат функции для тех же аргументов — на время вызова или на время сеанса.

Это правильный ответ на «функция ходит в базу за одним и тем же двести раз». Но у него есть цена: результат устаревает. Функция, вернувшая курс валюты, будет отдавать его и после того, как курс поменяли.

Поэтому так делают для того, что меняется редко: настройки, служебные ссылки, описания метаданных. Для остатков и цен — никогда.

Диалоги на клиенте отвечают не сразу

Это ломает голову всем, кто пришёл из обычных форм. В управляемом приложении Вопрос и Предупреждение не возвращают ответ: код не ждёт человека, а идёт дальше.

Ответ приходит в отдельную процедуру — оповещение. Её имя передают в ПоказатьВопрос, и продолжение кода пишут уже в ней.

Причина в веб-клиенте: браузер не умеет остановить выполнение и ждать. Поэтому и в тонком клиенте всё сделано одинаково — асинхронно.

✓ Так — как в обычных формах

Ответ = Вопрос("Записать?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
	Записать();
КонецЕсли;

В управляемом приложении так нельзя: в веб-клиенте это просто не работает, а код идёт дальше, не дождавшись человека.

✕ Так — с оповещением

Опис = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект);
ПоказатьВопрос(Опис, "Записать?", РежимДиалогаВопрос.ДаНет);

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

Продолжение кода живёт в отдельной процедуре. Выглядит длиннее — зато работает и в тонком клиенте, и в браузере.

Асинхронное всё, не только вопросы

По тому же правилу устроены выбор файла (НачатьПомещениеФайла), открытие формы с ожиданием результата (ОткрытьФорму с оповещением), выбор даты и цвета.

Узнать их легко по приставке: Показать…, Начать… — асинхронные, ответ придёт в оповещение. В новых версиях есть и Асинх-функции с Ждать, которые пишутся короче, но смысл тот же: код не останавливается.

Привилегированный режим

Иногда коду нужно сделать то, на что у пользователя прав нет: записать служебный регистр, прочитать настройку, до которой его роль не допускает.

Для этого есть УстановитьПривилегированныйРежим(Истина) и модули с флагом «Привилегированный». Внутри такого режима права не проверяются вовсе.

Правило обращения простое: включать на минимальный кусок и выключать сразу. Привилегированный режим, включённый «на всякий случай» в начале большой процедуры, — это дыра, через которую пользователь увидит чужие данные и не узнает об этом.

Упражнение 2. Где выполнять этот код

Напишите Куда(Задача): по описанию работы вернуть директиву. «запрос»&НаСервереБезКонтекста; «элементы»&НаСервере (нужен контекст формы); «вопрос»&НаКлиенте. Проверка спрашивает про «запрос».

Решается в тренажёре: код запускается и проверяется сразу.

Что именно едет по сети

«Вызов сервера» — это не мгновенный переход, а упаковка данных, отправка, распаковка на той стороне и то же самое обратно.

При &НаСервере едет весь контекст формы: реквизиты, табличные части, состояние элементов. На форме документа с тысячей строк это заметно, и заметно дважды — туда и обратно.

При &НаСервереБезКонтекста едут только параметры и результат. Поэтому такие вызовы и предпочитают: не из-за чистоты кода, а из-за объёма.

И третье, о чём забывают: не всё вообще можно отправить. Таблица значений несериализуема — вернуть её с сервера на клиент нельзя, про это была глава «Таблица значений».

Долгая операция в вызове сервера

Пока серверный вызов не закончился, форма у человека стоит. Обработка на пять минут внутри &НаСервере — это пять минут замершего окна и ощущение, что программа зависла.

Для долгого есть фоновые задания: запустить, вернуть управление форме, а результат забрать по готовности. В БСП для этого есть готовый механизм длительных операций.

Признак, что пора туда: если операция может занять больше нескольких секунд на реальных данных — а не на ваших трёх тестовых строках.

Упражнение 1. Сколько вызовов сервера

Функция ВызововСервера(Строк, ПоОдной) считает, сколько переходов на сервер потребуется. Если ПоОдной истина — по вызову на каждую строку. Если ложь — один вызов на все. Строк ноль — вызовов ноль в обоих случаях.

Решается в тренажёре: код запускается и проверяется сразу.

Короткий свод

Клиент видит человека и не видит базу; сервер наоборот. Директива говорит, где выполняется процедура и везти ли с собой форму. Без контекста — быстрее, и выбирают его, если форма целиком не нужна. Вызов в цикле — главная причина медленных форм: собрать, сходить один раз, разложить. Спрашивать человека можно только с клиента.

Как закрепить

Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.

Открыть эту главу в тренажёре

Модули: какой когда выполняется
Формы: реквизиты, элементы, события