Как ответить
Race condition (состояние гонки) возникает, когда два или более параллельных процесса одновременно читают и записывают одно и то же изменяемое состояние без синхронизации. Результат работы становится непредсказуемым — он зависит от порядка, в котором операционная система или среда выполнения планирует операции. Чаще всего это проявляется в многопоточных программах, но и в однопоточном JavaScript с асинхронными коллбеками и общим состоянием может возникнуть логическая гонка, хотя и без data race.
Классический пример — инкремент счётчика:
let counter = 0;
function increment() {
counter += 1;
}
// Запускаем две параллельные задачи
setTimeout(increment, 100);
setTimeout(increment, 100);Операция counter += 1 на самом деле состоит из трёх шагов: чтение текущего значения, прибавление единицы, запись результата. Если два потока (в браузере — Web Workers с SharedArrayBuffer, в Node.js — Worker threads) выполнят эту операцию одновременно, возможен сценарий: оба читают 0, оба прибавляют 1 и записывают 1. Итоговое значение станет 1, хотя мы ожидали 2.
Основные причины, из которых возникает race condition:
- Отсутствие синхронизации — критические секции не защищены мьютексами, атомарными операциями или локерами.
- Неатомарные составные операции — read-modify-write, check-then-act и т.д.
- Разделяемое состояние — разные потоки используют общие переменные, а не передают данные через message passing.
- Предположение о порядке выполнения — код рассчитывает, что задача A завершится раньше задачи B, хотя это не гарантируется.
- Повторное создание одинаковых событий (например, быстрый double-click отправляет два запроса, которые обрабатываются параллельно).
В веб-разработке race conditions часто встречаются в сценариях загрузки/сохранения: пользователь дважды нажимает кнопку «Отправить», и сервер обрабатывает оба запроса как независимые, создавая дубликаты. Или при загрузке данных с сервера — первый ответ ещё не обработан, а второй уже пришёл, и состояние UI разрушается.
Чтобы избежать проблемы, используют атомарные операции (в JS — Atomics.add, Atomics.store), мьютексы (через блокировки или специальные библиотеки), immutable-структуры или полное исключение общего состояния (каждый поток работает со своей копией). В UI-слое помогают флаги блокировки (дизаблим кнопку после первого клика) и идемпотентность операций на сервере.