Fetch API
Fetch — современный стандарт HTTP-запросов из браузера, построенный на Promises; ключевая ловушка: fetch не отклоняет Promise при HTTP-ошибках 4xx/5xx — это один из самых частых источников production-багов и проверяется на каждом собеседовании.
// Правильная обработка ошибок: проверяем res.okasync function apiFetch(url, options = {}) { const res = await fetch(url, { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options, }); if (!res.ok) { // !ok = статус >= 400 const err = new Error(`HTTP ${res.status}: ${res.statusText}`); err.status = res.status; try { err.body = await res.json(); } catch {} throw err; } return res.json();}
// POST с JSONawait apiFetch('/api/users', { method: 'POST', body: JSON.stringify({ name: 'Alice', role: 'admin' }),});
// Заголовки ответаconst res = await fetch('/api/resource');console.log(res.headers.get('Content-Type'));console.log(res.headers.get('X-RateLimit-Remaining')); // только если CORS-exposed// AbortController: таймаут и отменаasync function fetchWithTimeout(url, ms = 5000, options = {}) { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(new Error('Timeout')), ms); try { const res = await fetch(url, { ...options, signal: ctrl.signal }); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (e) { if (e.name === 'AbortError') throw new Error(`Request timed out after ${ms}ms`); throw e; } finally { clearTimeout(timer); }}
// React: отмена при unmountuseEffect(() => { const ctrl = new AbortController(); fetchWithTimeout('/api/data', 5000, { signal: ctrl.signal }) .then(setData) .catch(e => { if (e.name !== 'AbortError') setError(e); }); return () => ctrl.abort(); // cleanup при unmount/re-render}, []);// Streaming response (ReadableStream) — например, LLM-стримингasync function streamText(url, onChunk) { const res = await fetch(url); const reader = res.body.getReader(); const decoder = new TextDecoder(); try { while (true) { const { value, done } = await reader.read(); if (done) break; onChunk(decoder.decode(value, { stream: true })); } } finally { reader.releaseLock(); }}
// multipart/form-data (загрузка файлов)const form = new FormData();form.append('file', fileInput.files[0]);form.append('meta', JSON.stringify({ name: 'doc' }));// Content-Type: multipart/form-data + boundary выставляется браузером автоматическиawait fetch('/upload', { method: 'POST', body: form });
// credentials: управление cookies cross-origin// 'same-origin' (default) — cookies только для same-origin запросов// 'include' — всегда (нужно Access-Control-Allow-Credentials: true)// 'omit' — никогдаfetch('https://api.other.com/data', { credentials: 'include' });Итог: Fetch: network error → reject; HTTP 4xx/5xx → resolve (res.ok === false); AbortController управляет отменой; ReadableStream — download-стриминг; upload-прогресс через Fetch недоступен — нужен XHR.