Fetch API для AJAX запросов: полное руководство с примерами кода

#Web API  #Асинхронность  #Fetch API  
Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • JavaScript разработчики, стремящиеся улучшить свои навыки в работе с сетевыми запросами
  • Изучающие современные технологии для веб-программирования
  • Программисты, использующие или желающие перейти с XMLHttpRequest на Fetch API

JavaScript разработка долгое время страдала от неуклюжего XMLHttpRequest — того самого динозавра, с которым мы мучились годами для выполнения AJAX-запросов. Но наступила эра Fetch API — элегантного и мощного инструмента, который позволяет работать с сетевыми запросами так, как будто вы пишете стихи, а не код. Если вы всё ещё используете старые способы взаимодействия с API, эта статья станет вашим спасательным кругом в океане современного веб-программирования. Готовы навсегда изменить способ работы с сетевыми запросами? 🚀

Что такое Fetch API и почему она заменяет AJAX запросы

Fetch API — это современный интерфейс JavaScript для выполнения HTTP-запросов, который предоставляет более мощную и гибкую альтернативу старому доброму XMLHttpRequest (XHR). В основе Fetch лежит концепция Promise, что делает асинхронные операции значительно более читаемыми и поддерживаемыми.

Прежде чем мы погрузимся в технические детали, давайте разберемся, почему Fetch API постепенно вытесняет традиционные AJAX-запросы:

  • Современный синтаксис — более чистый, элегантный код с использованием Promise
  • Модульность — разделение запроса и ответа на отдельные объекты
  • Нативная поддержка — встроена в современные браузеры без необходимости дополнительных библиотек
  • Потоковая обработка данных — возможность работать с данными по мере их поступления
  • Упрощенная обработка JSON — встроенные методы для работы с JSON-ответами
Характеристика XMLHttpRequest Fetch API
Синтаксис Сложный, с множеством обратных вызовов Элегантный, основанный на Promise
Обработка ошибок Требует дополнительных проверок Унифицированный через цепочку .catch()
Поддержка JSON Ручной парсинг через JSON.parse() Встроенный метод .json()
Отмена запроса Встроенная функция xhr.abort() Через AbortController (более новые браузеры)
Поддержка в браузерах Все браузеры Все современные браузеры (IE требует полифил)

Антон Кравцов, Lead Frontend Developer

Когда я присоединился к проекту по редизайну платформы онлайн-образования, код был заполнен jQuery AJAX-запросами. Это была настоящая головная боль — разобраться в многочисленных вложенных функциях обратного вызова и "адском треугольнике" из скобок. Первое, что я сделал — перевёл все запросы на Fetch API.

Результат превзошёл ожидания: код стал более плоским, читаемым и понятным для новых членов команды. Обработка ошибок упростилась благодаря цепочкам промисов. Интеграция с нашим API стала настолько прозрачной, что мы смогли быстрее выпускать новые функции. Самое забавное — размер бандла уменьшился на 15%, просто потому что мы избавились от jQuery, который использовался в основном для AJAX.

Пошаговый план для смены профессии

Основной синтаксис Fetch API и создание простых запросов

Основное очарование Fetch API заключается в его простоте и выразительности. Давайте разберёмся с базовым синтаксисом и рассмотрим, как создавать различные типы запросов.

Базовый синтаксис Fetch выглядит так:

JS
Скопировать код
fetch(url, options)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка:', error));

Здесь url — адрес ресурса, к которому мы обращаемся, а options — необязательный объект с настройками запроса. Давайте рассмотрим основные типы запросов.

GET-запрос — самый простой тип запроса, используемый для получения данных:

JS
Скопировать код
// Простой GET-запрос
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка при получении данных:', error));

POST-запрос — используется для отправки данных на сервер:

JS
Скопировать код
// POST-запрос с JSON-данными
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Пользователь',
email: 'user@example.com'
})
})
.then(response => response.json())
.then(data => console.log('Успешный ответ:', data))
.catch(error => console.error('Ошибка:', error));

Другие типы запросов (PUT, DELETE и т.д.) создаются аналогичным образом, просто изменяя значение параметра method. Вот полный список основных HTTP-методов, которые вы можете использовать с Fetch API:

  • GET — получение данных (используется по умолчанию)
  • POST — отправка данных для создания нового ресурса
  • PUT — обновление существующего ресурса полностью
  • PATCH — частичное обновление ресурса
  • DELETE — удаление ресурса
  • OPTIONS — получение информации о поддерживаемых методах
  • HEAD — получение только заголовков ответа без тела

Async/await синтаксис делает работу с Fetch API еще более элегантной и читаемой:

JS
Скопировать код
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Произошла ошибка:', error);
}
}

fetchData();

Этот синтаксис особенно полезен при выполнении нескольких последовательных запросов или при сложной логике обработки данных. 🔄

Обработка ответов и преобразование данных в Fetch API

Одно из ключевых преимуществ Fetch API — элегантная работа с ответами сервера. Когда метод fetch() выполняется, он возвращает Promise, который разрешается в объект Response, представляющий HTTP-ответ.

Объект Response предоставляет множество полезных методов для обработки полученных данных:

  • response.json() — преобразует ответ в JSON-объект
  • response.text() — получает ответ в виде текста
  • response.blob() — работа с бинарными данными (изображения, файлы)
  • response.formData() — получает ответ в виде объекта FormData
  • response.arrayBuffer() — получает низкоуровневое представление данных

Важно понимать, что эти методы также возвращают Promise, поэтому требуется дополнительная обработка через .then() или await.

Рассмотрим пример обработки JSON-ответа:

JS
Скопировать код
fetch('https://api.example.com/users')
.then(response => {
// Проверка успешности ответа
if (!response.ok) {
throw new Error(`Ошибка HTTP: ${response.status}`);
}
return response.json(); // Преобразование в JSON
})
.then(data => {
// Работа с JSON-данными
console.log('Получены пользователи:', data);
// Дальнейшая обработка данных...
})
.catch(error => console.error('Ошибка получения или обработки данных:', error));

Для работы с бинарными данными, например, при загрузке изображения:

JS
Скопировать код
fetch('https://example.com/image.jpg')
.then(response => response.blob())
.then(blob => {
const imageUrl = URL.createObjectURL(blob);
const imgElement = document.createElement('img');
imgElement.src = imageUrl;
document.body.appendChild(imgElement);
})
.catch(error => console.error('Ошибка при загрузке изображения:', error));

Важно отметить при работе с Response — проверка статуса ответа. Fetch API не отклоняет Promise даже при получении HTTP-ошибки (например, 404 или 500). Promise будет отклонён только при сетевой ошибке. Поэтому необходимо явно проверять свойство response.ok или response.status:

JS
Скопировать код
fetch('https://api.example.com/nonexistent')
.then(response => {
if (!response.ok) {
throw new Error(`Ошибка HTTP: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Проблема с запросом:', error));

Мария Соколова, Frontend Team Lead

В одном из проектов нам требовалось реализовать функционал загрузки и отображения PDF-документов. Изначально я попыталась использовать старый подход с XMLHttpRequest, настраивая тип ответа как 'arraybuffer', но код получался неуклюжим и трудноподдерживаемым.

Решение пришло, когда я переписала логику на Fetch API. Мы загружали PDF как Blob и создавали URL-объект для отображения. Самое приятное открытие — мы смогли добавить индикатор прогресса загрузки, используя Response.body в качестве ReadableStream. Это было невозможно с XMLHttpRequest без дополнительных хаков.

Пользователи сразу заметили разницу: документы начали отображаться постепенно, по мере загрузки, а не ждали полной загрузки файла. Особенно это оценили клиенты с медленным интернетом. Такие моменты заставляют ценить современные API браузера!

Продвинутые техники работы с Fetch: заголовки и опции

Для серьезной работы с API необходимо уметь настраивать запросы, добавлять заголовки и изменять различные параметры. Fetch API предоставляет богатый набор опций для тонкой настройки HTTP-запросов. 🔧

Рассмотрим основные опции, которые можно передать в метод fetch():

Параметр Описание Возможные значения
method HTTP-метод запроса 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', и т.д.
headers Объект с HTTP-заголовками {'Content-Type': 'application/json', ...}
body Тело запроса (для POST/PUT/PATCH) JSON.stringify(data), FormData, Blob, и т.д.
mode CORS режим 'cors', 'no-cors', 'same-origin'
credentials Отправка куки и авторизации 'include', 'same-origin', 'omit'
cache Управление кэшированием 'default', 'no-cache', 'reload', 'force-cache', 'only-if-cached'
redirect Обработка перенаправлений 'follow', 'error', 'manual'
referrer Заголовок Referrer URL или пустая строка
signal Объект AbortSignal для отмены запроса AbortController.signal

Работа с заголовками

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

JS
Скопировать код
// Запрос с авторизационными заголовками
fetch('https://api.example.com/protected-data', {
headers: {
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV...',
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Custom-Header': 'CustomValue'
}
})
.then(response => response.json())
.then(data => console.log(data));

Вы также можете получить заголовки из ответа:

JS
Скопировать код
fetch('https://api.example.com/data')
.then(response => {
// Получаем отдельный заголовок
console.log('Content-Type:', response.headers.get('Content-Type'));

// Перебираем все заголовки
response.headers.forEach((value, name) => {
console.log(`${name}: ${value}`);
});

return response.json();
});

Отправка различных типов данных

Fetch API позволяет отправлять разные форматы данных. Вот несколько примеров:

  1. Отправка JSON-данных:
JS
Скопировать код
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Иван Петров',
email: 'ivan@example.com'
})
});

  1. Отправка данных формы:
JS
Скопировать код
const formData = new FormData();
formData.append('username', 'user123');
formData.append('avatar', fileInput.files[0]);

fetch('https://api.example.com/profile', {
method: 'POST',
body: formData
// Не указывайте Content-Type при использовании FormData,
// браузер установит его автоматически с правильной boundary
});

  1. Отправка URL-encoded данных (как из обычной HTML-формы):
JS
Скопировать код
fetch('https://api.example.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'username=user123&password=securepass'
});

Кросс-доменные запросы и CORS

При работе с разными доменами важно понимать CORS (Cross-Origin Resource Sharing). Вы можете настроить режим CORS с помощью опции mode:

JS
Скопировать код
fetch('https://other-domain.com/api/data', {
mode: 'cors', // Явно указываем режим CORS
credentials: 'include' // Отправлять куки при кросс-доменных запросах
});

  • cors — стандартный режим, требующий поддержки CORS на сервере
  • no-cors — ограниченный режим для запросов к серверам без поддержки CORS (нельзя прочитать содержимое ответа)
  • same-origin — запрещает кросс-доменные запросы

Параметр credentials определяет, будут ли отправляться куки и данные авторизации при запросе:

  • omit — не отправлять куки
  • same-origin — отправлять только при запросах на тот же домен
  • include — отправлять куки даже при кросс-доменных запросах (требует специальной настройки на сервере)

Обработка ошибок и отмена запросов в Fetch API

Грамотная обработка ошибок и возможность отмены запросов — важные аспекты при работе с сетевыми операциями. Fetch API предлагает элегантные решения для этих задач.

Комплексная обработка ошибок

Как уже упоминалось, Fetch не отклоняет Promise при получении HTTP-ошибки (404, 500 и т.д.), а только при сетевых проблемах. Рекомендуется использовать следующую структуру для надежной обработки всех типов ошибок:

JS
Скопировать код
function fetchWithErrorHandling(url, options = {}) {
return fetch(url, options)
.then(response => {
if (!response.ok) {
// Создаём расширенный объект ошибки с дополнительной информацией
const error = new Error(`HTTP error! Status: ${response.status}`);
error.status = response.status;
error.response = response;
throw error; // Прокидываем ошибку в catch
}
return response.json();
})
.catch(error => {
// Здесь обрабатываем как сетевые ошибки, так и HTTP-ошибки
if (error.status) {
console.error(`Ошибка HTTP ${error.status}: ${error.message}`);

// Специфическая обработка различных HTTP-статусов
switch (error.status) {
case 401:
console.log('Необходимо авторизоваться');
// Перенаправляем на страницу логина
break;
case 403:
console.log('Доступ запрещён');
break;
case 404:
console.log('Ресурс не найден');
break;
case 500:
console.log('Ошибка сервера');
break;
}
} else {
// Сетевая ошибка или другая проблема
console.error('Сетевая ошибка:', error.message);
}

// Пробрасываем ошибку дальше или возвращаем fallback-данные
throw error; // или return defaultData;
});
}

// Использование
fetchWithErrorHandling('https://api.example.com/data')
.then(data => console.log('Данные получены:', data))
.catch(error => {
// Сюда попадут только ошибки, которые не обработаны в fetchWithErrorHandling
console.error('Критическая ошибка:', error);
});

Отмена запросов с помощью AbortController

Современные браузеры поддерживают AbortController API, который позволяет отменять запросы Fetch:

JS
Скопировать код
const controller = new AbortController();
const signal = controller.signal;

// Запускаем запрос с сигналом
fetch('https://api.example.com/large-data', { signal })
.then(response => response.json())
.then(data => console.log('Данные получены:', data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Запрос был отменён пользователем');
} else {
console.error('Другая ошибка:', error);
}
});

// Где-то в другом месте кода (например, при нажатии кнопки "Отмена")
// controller.abort();

// Пример с таймаутом
setTimeout(() => {
controller.abort(); // Отмена запроса через 5 секунд
console.log('Запрос отменён из-за таймаута');
}, 5000);

Реализация таймаута для запросов

Fetch API не имеет встроенной поддержки таймаутов, но их можно реализовать с помощью AbortController и Promise.race():

JS
Скопировать код
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const { signal } = controller;

// Создаём Promise, который отклонится через timeout миллисекунд
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
controller.abort();
reject(new Error(`Запрос превысил таймаут в ${timeout}мс`));
}, timeout);
});

// Запускаем гонку между fetch и таймаутом
return Promise.race([
fetch(url, { ...options, signal }),
timeoutPromise
]);
}

// Использование
fetchWithTimeout('https://api.example.com/data', {}, 3000)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка или таймаут:', error.message));

Повторные попытки при сбоях

При работе с нестабильными API полезно реализовать механизм повторных попыток:

JS
Скопировать код
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let error;

for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
return await response.json();
} catch (err) {
error = err;
console.log(`Попытка ${i + 1} неудачна, ${maxRetries – i – 1} осталось`);

// Увеличиваем время ожидания между попытками (экспоненциальная задержка)
const delay = Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}

// Если все попытки неудачны, выбрасываем последнюю ошибку
throw error;
}

Грамотная обработка ошибок и управление жизненным циклом запросов — это то, что отличает профессиональный код от любительского. Применение этих техник делает ваши приложения более устойчивыми к сбоям и улучшает пользовательский опыт. 🛡️

Fetch API — это не просто замена устаревшему XMLHttpRequest, а настоящий прорыв в области сетевых взаимодействий JavaScript. Она предлагает более чистый, логичный и мощный подход к работе с HTTP-запросами. Обладая интеграцией с современными концепциями JavaScript, такими как Promise и async/await, Fetch API позволяет писать более поддерживаемый и понятный код. Даже если вы только начинаете освоение этой технологии, польза от перехода на Fetch API будет очевидна с первых строк кода. Помните, что хороший разработчик не просто использует инструменты, а понимает, почему и как они работают. Дополните ваш арсенал этим мощным API и поднимите качество вашего кода на новый уровень.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что делает метод fetch() в Fetch API?
1 / 5

Тимур Голубев

веб-разработчик

Свежие материалы

Загрузка...