Fetch API — это инструмент для выполнения сетевых запросов в веб-приложениях. При базовом использовании fetch()
— довольно простой метод, но у него есть много нюансов. Например, мы не можем прервать fetch-запрос.
В этой статье рассмотрим сценарии использования fetch()
вместе с другой возможностью языка — синтаксисом async/await
. Разберемся, как получать данные, обрабатывать ошибки и отменять запросы.

Введение в fetch()
Fetch API позволяет делать HTTP-запросы (GET, POST и т. д.) и обмениваться данными с сервером. Это более удобный аналог XHR.
Чтобы выполнить запрос, просто вызовите функцию fetch()
:
const response = await fetch(resource[, options]);
- Первый параметр
resource
— это URL-адрес запроса и объект Request. - Второй (необязательный) параметр
options
— это конфигурация запроса. Можно настроитьmethod
,header
,body
,credentials
и другие опции.
Функция fetch
выполняет запрос и возвращает промис, который будет ждать, когда запрос завершится. После этого промис выполняется (resolve
) с объектом Response (ответ сервера). Если во время запроса произошла какая-то ошибка, промис переходит в состояние rejected
.
Синтаксис async/await
прекрасно сочетается с fetch()
и помогает упростить работу с промисами. Давайте для примера сделаем запрос списка фильмов:
async function fetchMovies() {
const response = await fetch('/movies');
// ждем выполнения запроса
console.log(response);
}
Функция fetchMovies
асинхронная, используем для ее создания ключевое слово async
. Внутри она использует await
, чтобы дождаться выполнения асинхронной операции fetch
.
Внутри функции выполняется запрос на урл /movies
. Когда он успешно завершается, мы получаем объект response
с ответом сервера. Дальше в статье мы разберемся, как извлечь данные из этого объекта.
Получение JSON
Из объекта response
, который возвращается из await fetch()
можно извлечь данные в нескольких разных форматах. Чаще всего используется JSON:
async function fetchMoviesJSON() {
const response = await fetch('/movies');
const movies = await response.json();
return movies;
}
fetchMoviesJSON().then(movies => {
movies; // полученный список фильмов
});
Итак, чтобы извлечь полученные данные в виде JSON, нужно использовать метод response.json()
. Этот метод возвращает промис, так что придется снова воспользоваться синтаксисом await
, чтобы дождаться его выполнения: await response.json()
.
Кроме того, у объекта Response есть еще несколько полезных методов (все методы возвращают промисы):
response.json()
возвращает промис, который резолвится в JSON-объект;response.text()
возвращает промис, который резолвится в обычный текст;response.formData()
возвращает промис, который резолвится в объект FormData;response.blob()
возвращает промис, который резолвится в Blob (файлоподобный объект с необработанными данными);response.arrayBuffer()()
возвращает промис, который резолвится в ArrayBuffer (необработанные двоичные данные).
Обработка ошибок
Для разработчиков, которые только начинают работать с fetch
, может быть непривычным то, что этот метод не выбрасывает исключение, если сервер возвращает «плохой» HTTP-статус (клиентские 400-499 или серверные 500-599 ошибки).
Попробуем для примера обратиться к несуществующей странице /oops
. Этот запрос, как и ожидается, завершается со статусом 404
.
async function fetchMovies404() {
const response = await fetch('/oops');
response.ok; // => false
response.status; // => 404
const text = await response.text();
return text;
}
fetchMovies404().then(text => {
text; // => 'Page not found'
});
Из объекта response
мы можем узнать, что запрос не удался, однако метод fetch()
не выбрасывает ошибку, а считает этот запрос завершенным.
Промис, возвращаемый функцией fetch
, отклоняется только в том случае, если запрос не может быть выполнен или ответ не может быть получен, например, из-за проблем с сетью (нет подключения, хост не найден, сервер не отвечает).
Но к счастью, у нас есть поле response.ok
, с помощью которого мы можем отловить плохие статусы. Оно принимает значение true
только если статус ответа 200-299
.
Если вы хотите получать ошибку для всех неудачных запросов, просто выбрасывайте ее вручную:
async function fetchMoviesBadStatus() {
const response = await fetch('/oops');
if (!response.ok) {
const message = `An error has occured: ${response.status}`;
throw new Error(message);
}
const movies = await response.json();
return movies;
}
fetchMoviesBadStatus().catch(error => {
error.message; // 'An error has occurred: 404'
});
Отмена fetch-запроса
К сожалению, Fetch API не предоставляет нам никакой возможности отменить запущенный запрос. Но это можно сделать с помощью AbortController.
Чтобы объединить эти инструменты, нужно сделать 3 действия:
// Шаг 1. Создать экземпляр AbortController до начала запроса
const controller = new AbortController();
// Step 2: Передать в параметры запроса fetch() controller.signal
fetch(..., { signal: controller.signal });
// Step 3: Отменить запрос методом controller.abort при необходимости
controller.abort();
Давайте для примера создадим маленькое приложение с двумя кнопками, одна из которых будет запускать fetch-запрос, а вторая прерывать его.
let controller = null;
// Обрабатываем клики по первой кнопке
fetchMoviesButton.addEventListener('click', async () => {
controller = new AbortController();
try {
const response = await fetch('/movies', {
signal: controller.signal
});
} catch (error) {
console.log('Fetch error: ', error);
}
controller = null;
});
// Обрабатываем клики по второй кнопке
cancelFetchButton.addEventListener('click', () => {
if (controller) {
controller.abort();
}
});
Демо:
Кликните по кнопке Fetch movies, чтобы запустить запрос, а затем по кнопке Cancel fetch, чтобы отменить его. При этом возникнет ошибка, которую поймает блок .catch()
.
Экземпляр AbortController
одноразовый, его нельзя переиспользовать для нескольких запросов. Поэтому для каждого вызова fetch
нужно создавать новый инстанс.
Параллельные fetch запросы
Чтобы выполнять fetch-запросы параллельно, можно воспользоваться методом Promise.all()
.
Например, запустим сразу два запроса — для получения фильмов и для получения категорий фильмов:
async function fetchMoviesAndCategories() {
const [moviesResponse, categoriesResponse] = await Promise.all([
fetch('/movies'),
fetch('/categories')
]);
const movies = await moviesResponse.json();
const categories = await categoriesResponse.json();
return [movies, categories];
}
fetchMoviesAndCategories().then(([movies, categories]) => {
movies; // список фильмов
categories; // список категорий
}).catch(error => {
// один из запросов завершился с ошибкой
});
Фрагмент кода await Promise.all([])
запускает запросы параллельно и ожидает, когда все они перейдут в состояние resolved
.
Если один из запросов завершится с ошибкой, то Promise.all
тоже выбросит ошибку.
Если же вы хотите, чтобы выполнились все запросы, даже если несколько из них упали, используйте метод Promise.allSettled().
Заключение
Вызов функции fetch()
запускает запрос к серверу и возвращает промис. Когда запрос успешно завершается, промис переходит в состояние resolved
и возвращает объект ответа (response
), из которого можно извлечь данные в одном из доступных форматов (JSON, необработанный текст или Blob).
Так как fetch
возвращает промис, мы можем использовать синтаксис async/await
, чтобы упростить код: response = await fetch()
.
В статье мы разобрали, как использовать эту комбинацию для получения данных, обработки ошибок, отмены запросов и выполнения параллельных запросов.
0 комментариев