Как сделать параллельные запросы и получить результаты вместе для рендера
Что проверяют
Понимаете ли вы, как запустить запросы одновременно, а не цепочкой, и что делать, когда один из них упал. Следом обычно спрашивают про отмену при уходе со страницы и про частичный рендер.
Как отвечать
Базовый каркас: const [user, orders] = await Promise.all([getUser(id), getOrders(id)]). Запросы стартуют в момент вызова функций, а Promise.all ждёт все и отдаёт результаты в том же порядке, в каком были промисы, независимо от того, какой ответ пришёл первым.
Дальше раскройте детали:
- быстрый отказ:
Promise.allотклоняется на первой же ошибке, остальные запросы при этом не отменяются, а их результаты теряются; - частичный результат:
Promise.allSettledдождётся всех и вернёт массив объектов соstatus: 'fulfilled'или'rejected'. Так можно отрисовать страницу и показать заглушку только в сломавшемся блоке; - ошибки HTTP:
fetchне отклоняет промис на 404 или 500, проверяйтеresponse.okи бросайте ошибку сами, иначеPromise.all«успешно» вернёт тело ошибки; - отмена: один
AbortControllerна группу, егоsignalпередаётся в каждыйfetch, аabort()вызывается в cleanup уuseEffect. Для таймаута естьAbortSignal.timeout(ms); - в React-приложении: TanStack Query даёт
useQueriesиuseSuspenseQueriesс кешем и повторами, а в серверных компонентах Next.js данные собирают тем жеPromise.allв async-компоненте.
Если запросов десятки, стоит упомянуть ограничение параллельности: браузер на HTTP/1.1 держит около шести соединений на хост, остальные встают в очередь, а с HTTP/2 они мультиплексируются в одном соединении.
Где спотыкаются
Пишут const a = await getA(); const b = await getB(); и называют это параллельным. Это водопад: второй запрос стартует только после первого.
Передают в Promise.all функции вместо промисов, например [getA, getB]. Запросы не выполняются, а в результатах оказываются сами функции.
Не обрабатывают гонку: пользователь переключил вкладку, старые ответы пришли позже новых и перезаписали состояние. Спасает отмена через AbortController или проверка, что ответ относится к актуальному запросу.