Fetch API: запросы, ответы и AbortController
Fetch API
Базовый fetch
// GET запрос
const response = await fetch('/api/users')
const users = await response.json()
// POST запрос
const newUser = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ name: 'Иван', email: 'ivan@example.com' })
}).then(r => r.json())
Объект Response
const response = await fetch('/api/users')
// Статус и заголовки
response.status // 200, 404, 500...
response.ok // true если status 200-299
response.statusText // 'OK', 'Not Found'...
response.headers.get('Content-Type') // 'application/json'
// Чтение тела (каждый метод можно вызвать только ОДИН раз!)
await response.json() // JSON
await response.text() // строка
await response.blob() // Blob (файлы, изображения)
await response.formData() // FormData
await response.arrayBuffer() // бинарные данные
fetch НЕ выбрасывает исключение при HTTP ошибках (404, 500)! Он выбрасывает только при сетевых ошибках. Всегда проверяйте response.ok.Правильная обработка ошибок
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`)
if (!response.ok) {
// HTTP ошибка — создаём исключение вручную
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return await response.json()
} catch (error) {
if (error.name === 'TypeError') {
// Сетевая ошибка (нет соединения, CORS и т.д.)
throw new Error('Ошибка сети: ' + error.message)
}
throw error
}
}
AbortController — отмена и таймауты
// Таймаут запроса
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const response = await fetch(url, {
signal: controller.signal
})
clearTimeout(timeoutId)
return response
} catch (error) {
if (error.name === 'AbortError') {
throw new Error(`Таймаут: запрос к ${url} превысил ${timeout}мс`)
}
throw error
}
}
// Отмена предыдущего запроса при новом (поиск)
let searchController = null
async function search(query) {
// Отменяем предыдущий запрос
if (searchController) {
searchController.abort()
}
searchController = new AbortController()
try {
const response = await fetch(`/api/search?q=${query}`, {
signal: searchController.signal
})
return response.json()
} catch (error) {
if (error.name === 'AbortError') {
return null // Запрос был отменён — нормальная ситуация
}
throw error
}
}
CORS (Cross-Origin Resource Sharing) — механизм безопасности браузера. Для запросов к другому домену сервер должен вернуть заголовок Access-Control-Allow-Origin.
// Preflight запрос — браузер автоматически отправляет OPTIONS
// перед POST/PUT/DELETE к другому домену
// Credentials (cookies, auth) — нужно явно указать
const response = await fetch('https://api.example.com/data', {
credentials: 'include' // или 'same-origin' (по умолчанию)
})
// Сервер должен вернуть: Access-Control-Allow-Credentials: true