Перейти к содержимому

XMLHttpRequest (legacy)

XHR — оригинальный AJAX API (IE5, 1999), предшественник Fetch; знание его API критично при работе со старыми кодовыми базами и при необходимости upload-прогресса, которого до сих пор нет в Fetch; спрашивают для понимания исторического контекста и при разборе поведения withCredentials.

// Базовый XHR, завёрнутый в Promise
function xhrFetch(url, options = {}) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method ?? 'GET', url, true); // true = async
Object.entries(options.headers ?? {}).forEach(
([k, v]) => xhr.setRequestHeader(k, v)
);
xhr.responseType = 'json'; // автоматический JSON-парсинг
xhr.timeout = options.timeout ?? 10_000;
xhr.onload = () => xhr.status >= 200 && xhr.status < 300
? resolve(xhr.response)
: reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
xhr.onerror = () => reject(new Error('Network error'));
xhr.ontimeout = () => reject(new Error('Timeout'));
xhr.onabort = () => reject(new Error('Aborted'));
xhr.send(options.body ?? null);
options.xhrRef?.(xhr); // опциональный доступ к инстансу
});
}
// Upload с прогрессом — единственное реальное преимущество XHR перед Fetch
function uploadWithProgress(url, file, { onProgress, onDone, onError } = {}) {
const xhr = new XMLHttpRequest();
const form = new FormData();
form.append('file', file);
xhr.open('POST', url);
xhr.withCredentials = true; // аналог credentials:'include'
xhr.upload.addEventListener('progress', e => {
if (e.lengthComputable) onProgress?.(Math.round(e.loaded / e.total * 100));
});
xhr.upload.addEventListener('error', () => onError?.(new Error('Upload failed')));
xhr.onload = () => onDone?.(JSON.parse(xhr.responseText));
xhr.onerror = () => onError?.(new Error('Network error'));
xhr.send(form);
return () => xhr.abort(); // возвращаем функцию отмены
}
// Использование
const cancel = uploadWithProgress('/upload', file, {
onProgress: pct => setProgress(pct),
onDone: data => console.log('done', data),
});
// cancel() — прервать загрузку
// XHR vs Fetch: таблица сравнения
//
// Feature XHR Fetch
// ───────────────────── ───────────────────── ──────────────────────
// API style Event callbacks Promise-based
// Upload progress ✓ xhr.upload.onprogress ✗ (нет)
// Download streaming частично (onprogress) ✓ ReadableStream
// Abort xhr.abort() AbortController
// Intercepted by SW ✗ ✓
// Response types text/json/blob/arraybuf Stream / blob / json
// Node.js (native) ✗ ✓ (18+)
// HTTP/2 aware ✗ ✓
//
// Sync XHR (третий аргумент false) — устарел, блокирует main thread,
// запрещён в Service Workers, генерирует предупреждение в DevTools:
// xhr.open('GET', url, false); // НИКОГДА не использовать в production

Итог: XHR актуален только для upload-прогресса и поддержки legacy-кода; в новом коде используйте Fetch + AbortController.