Как неконтролируемый порядок выполнения асинхронных адаптеров влияет на данные
Что проверяют
Видите ли вы гонки там, где их обычно не ждут: в однопоточном JavaScript. Интервьюеру важно, чтобы вы понимали, что порядок завершения промисов не совпадает с порядком запуска, и умели это контролировать. Следом обычно просят показать, как вы защищаете поиск с подсказками или форму от устаревшего ответа.
Как отвечать
Под адаптерами обычно понимают функции, которые ходят в разные источники, приводят ответ к единой модели и записывают его в стор или состояние компонента. Если их запуск не упорядочен, возникают три типичных эффекта:
- устаревший ответ побеждает: пользователь ввёл «re», потом «react», второй запрос вернулся раньше, первый позже и перезаписал результат;
- last write wins: два адаптера пишут в один объект, итог зависит от того, кто закончил последним, и меняется от запуска к запуску;
- частичные данные: компонент рендерится по ответу первого адаптера, когда второй ещё не пришёл, и показывает несогласованную картину.
Способы контроля:
- отмена:
AbortControllerи передачаsignalвfetch, при новом запросе старый отменяется. Отмена приходит как отклонённый промис сAbortError, его нужно отфильтровать, а не показывать как ошибку; - флаг или счётчик версий: в
useEffectфункция очистки выставляетignore = true, а ответ применяется, только если его номер совпадает с последним; - библиотеки: TanStack Query привязывает ответ к ключу запроса, RxJS даёт
switchMap; - явная композиция:
Promise.allвозвращает результаты в порядке входного массива, а не завершения,Promise.allSettledне теряет остальные при одной ошибке. Для строгой последовательности подходитfor...ofсawait.
Где спотыкаются
Говорят, что в JS нет гонок, раз он однопоточный. Потоков нет, а переключение между задачами на каждом await есть, и этого достаточно.
Пишут array.forEach(async ...) и ждут последовательного выполнения. forEach не ждёт промисы, все вызовы стартуют сразу, а код после цикла выполняется раньше, чем они закончатся.
Лечат гонку через setTimeout или debounce. Задержка снижает частоту запросов, но не гарантирует порядок ответов, устаревший ответ всё равно может прийти последним.