Как обрабатывать согласие и отказ при асинхронных ответах
Что проверяют
Под «согласием и отказом» почти всегда имеют в виду два исхода промиса: выполнение (resolve) и отклонение (reject). Проверяют, умеете ли вы обработать оба так, чтобы интерфейс не завис в загрузке и ошибка не потерялась. Следом обычно спрашивают про несколько параллельных запросов и про отмену.
Как отвечать
Каркас ответа:
- два способа записи:
.then(onFulfilled).catch(onRejected)илиasync/awaitсtry/catch/finally. Вfinallyснимайте флаг загрузки, он выполнится при любом исходе; fetchне отклоняется на 404 и 500: промис отклоняется только при сетевом сбое или отмене. Статус проверяют черезresponse.okи сами бросают ошибку, иначе ответ с ошибкой уйдёт в ветку успеха;- несколько запросов:
Promise.allотклоняется на первом же отказе, остальные результаты теряются. Если нужны все исходы, беритеPromise.allSettled: он возвращает массив объектов соstatus'fulfilled'или'rejected'.Promise.anyждёт первого успеха,Promise.raceпервого завершения любого вида; - отмена:
AbortControllerиsignalвfetch. Отменённый запрос отклоняется сAbortError, его отличают и не показывают пользователю как сбой; - устаревшие ответы: в React при быстрой смене параметров старый ответ может прийти позже нового. Отменяйте запрос в функции очистки
useEffectили игнорируйте результат по флагу; - состояния интерфейса: загрузка, успех, пустой результат, ошибка с кнопкой повтора. Часто это отдают TanStack Query, где повторы и кеш уже встроены.
Необработанные отказы ловит событие unhandledrejection на window, туда удобно повесить отправку в систему сбора ошибок.
Где спотыкаются
Надеются только на catch у fetch и считают, что ответ 500 туда попадёт. Он придёт в ветку успеха с response.ok === false.
Возвращают null из catch и продолжают цепочку как ни в чём не бывало. Дальше код падает на data.items с непонятной ошибкой вдали от места сбоя.
Забывают finally или return внутри .then. Спиннер крутится вечно, а промис из вложенного вызова отклоняется без обработчика.