Будут ли серверные процедуры в 1С выполняться строго последовательно при вызове из клиентского кода

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

В процессе разработки на платформе 1С:Предприятие 8 часто возникает вопрос о гарантиях порядка выполнения программного кода. Типичная ситуация: разработчик создает клиентскую процедуру, внутри которой поочередно вызываются несколько серверных процедур. Возникает логичное опасение — может ли случиться так, что вторая или четвертая процедура начнет работу до завершения первой? Рассмотрим этот вопрос детально, разберем механизмы работы платформы и выясним, какие нюансы могут создать иллюзию нарушения последовательности.

Базовый принцип: Синхронность вызовов

Начнем с основ. По умолчанию в 1С вызовы серверных процедур и функций из клиентского контекста являются синхронными. Проанализируем, что это означает на практике для потока выполнения. Когда мы пишем код в модуле формы:


&НаКлиенте
Процедура ЗапуститьОбработкуДанных()
    
    // Шаг 1: Подготовка данных
    ПроцедураСервера1(); 
    
    // Шаг 2: Расчет показателей
    ПроцедураСервера2();
    
    // Шаг 3: Формирование движений
    ПроцедураСервера3();
    
КонецПроцедуры

Рассмотрим по шагам, как ведет себя система. В момент вызова ПроцедураСервера1() основной поток клиентского приложения приостанавливается. Пользователь видит "песочные часы" или просто не может взаимодействовать с интерфейсом. Клиент ждет, пока сервер полностью отработает весь код внутри первой процедуры, выполнит все запросы к базе данных и вернет управление. Только после того, как ПроцедураСервера1 завершится, интерпретатор перейдет к строке с ПроцедураСервера2(). Таким образом, строгая последовательность гарантирована архитектурой платформы.

Иллюзия параллельности и отложенная запись данных

Почему же у разработчиков иногда возникает ощущение, что данные во второй процедуре еще "не готовы", хотя первая уже отработала? Разберем основные технические причины, которые могут привести к такому выводу.

1. Транзакции и уровни изоляции. Рассмотрим ситуацию, когда ПроцедураСервера1 записывает документ или набор записей регистра. В 1С любая запись объекта инициирует транзакцию. Если внутри серверной процедуры не было явной ошибки, транзакция фиксируется (commit) в конце выполнения серверного вызова. Однако, если первая процедура делает запись, а вторая пытается прочитать эти данные, важно учитывать, происходят ли эти вызовы в рамках одного серверного контекста (одного сеанса) или разных. В рамках одного сеанса последовательность соблюдается, но если между процедурами происходит физический разрыв или используются механизмы, работающие вне основной транзакции, данные могут быть "невидимы" для СУБД на доли секунды.

2. Кэширование объектов. Это одна из самых частых причин "странного" поведения кода. Выясним причину: если в ПроцедураСервера1 вы изменили объект (например, справочник), а в ПроцедураСервера2 обращаетесь к этому же объекту через переменную, созданную ранее, вы можете получить данные из локального кэша, а не из актуальной базы данных. Разберем пример:


&НаСервере
Процедура ПроцедураСервера1(СсылкаНаОбъект)
    Объект = СсылкаНаОбъект.ПолучитьОбъект();
    Объект.Реквизит1 = "Новое значение";
    Объект.Записать();
КонецПроцедуры

&НаСервере
Процедура ПроцедураСервера2(СсылкаНаОбъект)
    // ОШИБКА: Если мы не перечитаем данные, можем увидеть старое значение
    Сообщить(СсылкаНаОбъект.Реквизит1); 
КонецПроцедуры

Для решения этой проблемы необходимо всегда использовать актуальные ссылки или принудительно перечитывать объекты, если логика программы предполагает их последовательное изменение в разных процедурах.

Фоновые задания: Когда параллельность становится реальной

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

ФоновыеЗадания.Выполнить("МойМодуль.ДлительнаяОперация", Параметры);

В этом случае ПроцедураСервера1 инициирует запуск нового процесса на сервере и мгновенно завершится, вернув управление на клиент. Клиент тут же запустит ПроцедураСервера2. В итоге "ДлительнаяОперация" и ПроцедураСервера2 будут выполняться одновременно. Если ваша логика во второй процедуре опирается на результат выполнения фонового задания из первой — вы получите ошибку или пустой результат, так как фоновый процесс, скорее всего, еще не успеет завершиться.

Асинхронные вызовы в современных версиях платформы (8.3.15+)

С развитием платформы 1С появились асинхронные методы, работающие через обещания (Promises). Посмотрим на пример кода, который может нарушить привычную последовательность:


&НаКлиенте
Асинхронная Процедура ЗапуститьВызовы()
    
    // Если вызвать асинхронный метод без "Ждать", код пойдет дальше
    ВыполнитьЗадачуАсинхронно(); 
    
    ПроцедураСервера2(); // Выполнится, не дожидаясь завершения первой!
    
КонецПроцедуры

Чтобы сохранить последовательность в асинхронном контексте, мы должны обязательно использовать ключевое слово Ждать (Await). Это заставит систему дождаться результата выполнения первой функции перед переходом к следующей строке.

Вложенные вызовы и структура стека

Рассмотрим еще один важный аспект, упомянутый в обсуждении — вложенность. Если ПроцедураСервера1 внутри себя вызывает ПроцедураСервера4, то порядок выполнения будет следующим:

  1. Начало ПроцедураСервера1.
  2. Выполнение вложенной ПроцедураСервера4.
  3. Завершение ПроцедураСервера1.
  4. Начало ПроцедураСервера2.

Здесь работает классический принцип стека (LIFO). Никакого "перемешивания" или одновременного выполнения кода разных процедур в одном потоке не происходит.

Рекомендации для обеспечения надежности

Проанализируем шаги, которые помогут вам избежать проблем с последовательностью и целостностью данных:

  1. Группировка операций: Если несколько процедур должны работать с одними и теми же данными в строгом порядке, рассмотрите возможность объединения их в одну общую серверную процедуру. Это не только гарантирует порядок, но и сократит количество "походов" с клиента на сервер (Client-Server Roundtrips), что существенно повысит производительность.
  2. Использование транзакций: Если критически важно, чтобы либо все изменения применились, либо ни одно из них, оберните вызовы в явную транзакцию с помощью операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию().
  3. Проверка завершения фоновых заданий: Если вы используете фоновые вычисления, обязательно добавьте механизмы ожидания (например, через обработчик ожидания на форме или метод ФоновыеЗадания.ОжидатьЗавершения()), прежде чем переходить к процедурам, использующим их результат.
  4. Перечитывание данных: При последовательном изменении одних и тех же ссылочных данных используйте метод Объект.Прочитать() или заново получайте объект из базы, чтобы исключить влияние кэша.

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

← На главную