Menu
Русский

Fetch API в JavaScript: запросы, JSON и обработка ошибок

Разбираемся с Fetch API в JavaScript: GET- и POST-запросы, парсинг JSON, грамотная обработка ошибок и отмена зависших запросов через AbortController.

Fetch — промис-ориентированный HTTP-клиент

fetch встроен в браузеры и современные версии Node. Передаёте ему URL — получаете Promise, который резолвится объектом Response. По сути, это и есть весь API:

index.js
Output
Click Run to see the output here.

Два вызова .then здесь не случайно — у нас два асинхронных шага. Сначала приходят заголовки ответа (именно этим резолвится первый промис), а уже потом читается и парсится тело ответа (response.json() сам по себе возвращает промис). Тело не загружается до тех пор, пока вы явно его не запросите.

Тот же самый код через async/await читается как обычный, сверху вниз:

index.js
Output
Click Run to see the output here.

Два await — две точки приостановки. Работа та же, но порядок чтения гораздо понятнее.

Объект Response

Важный момент: fetch возвращает вам не тело ответа, а объект Response — с метаданными и набором методов, которые позволяют прочитать тело в нужном формате:

index.js
Output
Click Run to see the output here.

Тело ответа можно прочитать через .json(), .text(), .blob(), .arrayBuffer() или .formData() — каждый из этих методов возвращает промис. Важный нюанс: тело читается ровно один раз. Если дважды вызвать .json() на одном и том же Response, второй вызов выбросит ошибку.

Главная ловушка: HTTP-ошибки не вызывают reject

На этом спотыкается почти каждый, кто только начинает работать с fetch. Ответ 404 или 500 — это не реджект промиса. Промис спокойно резолвится, просто response.ok будет равен false. Fetch отклоняется только тогда, когда сам запрос не удалось выполнить: упал DNS, нет сети, заблокировал CORS.

В итоге наивный код радостно проглотит страницу с ошибкой и уже потом упадёт на .json():

index.js
Output
Click Run to see the output here.

Чтобы это исправить, проверяйте response.ok вручную и выбрасывайте исключение, если сервер вернул код ошибки:

index.js
Output
Click Run to see the output here.

Привыкайте писать этот блок if (!response.ok) — он должен быть в каждой обёртке над fetch, которую вы пишете.

POST-запрос через fetch

По умолчанию fetch отправляет GET. Для любого другого метода передайте второй аргумент — объект с настройками:

index.js
Output
Click Run to see the output here.

Отметим три важных момента:

  • method по умолчанию — "GET". Для POST, PUT, DELETE и PATCH его нужно указать явно.
  • В body передаётся строка (или FormData, Blob и т.п.) — fetch сам объекты не сериализует. JSON.stringify(...) — ваша забота.
  • Заголовок Content-Type говорит серверу, как разбирать тело запроса. Забудете — и большинство серверов воспримут тело как обычный текст.

Заголовки, query-параметры и другие опции

Заголовки — это обычный объект (или экземпляр Headers). Query-строку собираем руками, чаще всего через URLSearchParams:

index.js
Output
Click Run to see the output here.

URLSearchParams сам позаботится о кодировании — пробелы, амперсанды, юникод, — так что URL не развалится, даже если во входных данных есть символы, требующие экранирования.

Ещё пара опций, которые часто встречаются в реальном коде: credentials: "include" — чтобы отправлять куки при кросс-доменных запросах, cache: "no-store" — чтобы обойти HTTP-кэш, и mode: "cors" (обычно стоит по умолчанию) — для управления поведением CORS.

Отмена запроса через AbortController

Бывает, что запрос нужно просто прервать — пользователь ввёл новый поисковый запрос или ответ слишком долго не приходит. Для этого в fetch есть AbortController:

index.js
Output
Click Run to see the output here.

controller.abort() приводит к тому, что промис fetch отклоняется с DOMException, у которого name равен "AbortError". Блок finally очищает таймер — чтобы после успешного запроса не остался висеть забытый setTimeout.

Такую связку — fetch, таймаут и очистка — имеет смысл завернуть в отдельную функцию и переиспользовать по всему проекту.

Переиспользуемая обёртка над fetch

Соберём всё вместе — получится небольшой хелпер, который берёт на себя всю рутину:

index.js
Output
Click Run to see the output here.

Одно место для заголовков, одно — для обработки ошибок, одно — для работы с пустыми ответами. В любом мало-мальски серьёзном приложении в итоге появляется что-то подобное.

Дальше: обработка ошибок в асинхронном коде

Fetch — одно из самых частых мест, где всплывают асинхронные ошибки, и проверка response.ok здесь лишь кусочек пазла. Следующая страница — про обработку ошибок в промисах и async/await: куда уходят ошибки, как их ловить и какие ловушки позволяют им молча проскользнуть мимо.

Часто задаваемые вопросы

Как пользоваться fetch в JavaScript?

Вызываете fetch(url) с нужным адресом — получаете Promise, который разрешится объектом Response. Чтобы достать тело ответа, вызывайте response.json() (это тоже промис). Через async/await выглядит так: const res = await fetch(url); const data = await res.json();.

Как отправить POST-запрос через fetch?

Вторым аргументом передайте объект с опциями: method: 'POST', заголовки (обычно 'Content-Type': 'application/json') и body. Объекты нужно вручную превратить в строку через JSON.stringify(...) — fetch сам тело не сериализует.

Почему fetch не падает на 404 или 500?

Fetch реджектит промис только при сетевых проблемах — DNS не разрешился, нет соединения, CORS заблокировал. Для fetch ответ со статусом 404 или 500 — это всё ещё успешно полученный ответ. Проверять нужно самому: response.ok (true при статусах 200–299) или response.status, и при ошибке кидать исключение вручную.

Можно ли отменить запрос fetch?

Да, через AbortController. Создаёте контроллер, в опциях fetch передаёте его signal, а когда нужно отменить запрос — вызываете controller.abort(). Промис fetch упадёт с AbortError, который ловится в обычном catch.

Учитесь программировать с Coddy

НАЧАТЬ