Как фронтенд гарантирует отправку последнего состояния на бэкэнд без блокировки пользователя?
Что проверяют
Понимаете ли вы две разные проблемы: гонку запросов, когда старый ответ затирает новый, и потерю данных при закрытии вкладки. Часто следом просят спроектировать автосохранение для редактора документов.
Как отвечать
Разложите ответ на уровни:
- не блокировать: запрос асинхронный, интерфейс обновляется оптимистично, а статус «сохраняется», «сохранено» или «ошибка» показывается ненавязчиво;
- не спамить: debounce на 500-1000 мс после последнего изменения плюс принудительная отправка раз в несколько секунд при непрерывном вводе;
- последнее побеждает: у каждого сохранения растущий номер версии. Предыдущий запрос отменяется через
AbortController, а сервер отклоняет запись со старой версией (оптимистическая блокировка через полеversionилиIf-MatchсETag). Отмена на клиенте не гарантирует, что запрос не дошёл, поэтому проверка на сервере обязательна; - одна очередь: не больше одного запроса в полёте, новые изменения копятся и уходят следующим пакетом;
- закрытие вкладки: на
visibilitychangeв состоянииhiddenили наpagehideотправка черезnavigator.sendBeaconилиfetchсkeepalive: true. Событияbeforeunloadиunloadненадёжны, особенно на мобильных. У обоих способов лимит на объём тела около 64 КБ; - офлайн и сбои: черновик в IndexedDB или
localStorage, повтор с экспоненциальной задержкой, досылка по событиюonline.
Web Workers к этой задаче почти не относятся: сетевой запрос и так не блокирует основной поток.
Где спотыкаются
Ставят только debounce и считают задачу решённой. Ответы приходят не по порядку, и медленный старый запрос перезаписывает свежие данные.
Надеются на beforeunload с обычным fetch. Браузер обрывает запрос при выгрузке страницы, и последние правки теряются.
Блокируют форму на время сохранения: неактивные поля и спиннер на весь экран. Пользователь ждёт сеть, хотя от него ничего не требуется.