XmlHttpRequest vs Fetch

Публікації
XmlHttpRequest vs Fetch

Для відправки HTTP-запитів на сервер часто використовуються технології XmlHttpRequest і Fetch API. У цій статті ми розглянемо обидві технології, їх переваги та недоліки, а також наведемо приклади використання.

XmlHttpRequest

XmlHttpRequest - це стара технологія, яка використовується для виконання асинхронних HTTP-запитів в JavaScript. Вона була основним способом виконання AJAX-запитів до появи Fetch API.

Переваги XmlHttpRequest

  • Широка підтримка браузерами, включаючи старі версії.
  • Дозволяє здійснювати синхронні запити (не рекомендується).

Недоліки XmlHttpRequest

  • Більш складний і незграбний синтаксис порівняно з Fetch API.
  • Може бути складно читати та підтримувати код.

Приклад використання XmlHttpRequest

var xhr = new XMLHttpRequest(); xhr.open('GET', 'https://api.example.com/data', true); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } }; xhr.send();

Fetch API

Fetch API - це сучасний інтерфейс для виконання HTTP-запитів, який забезпечує зручний і гнучкий спосіб роботи з сервером.

    Переваги Fetch API:
  • Більш простий і зрозумілий синтаксис.
  • Повертає проміси, що полегшує роботу з асинхронним кодом.

Недоліки Fetch API

  • Не підтримується деякими старими браузерами.
  • Не дозволяє здійснювати синхронні запити.

Приклад використання Fetch API

fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));
Функція XMLHttpRequest fetch
Підтримка обіцянок (Promises)
Простий синтаксис
*інколи так вважають
Підтримка синхронних запитів
Підтримка стримінгу (потоку) відповідей (response streaming)
(з використанням onprogress)
Обробка JSON автоматично
Підтримка скасування запитів
(з використанням AbortController)
Підтримка обробки прогресу
(з використанням ReadableStream)
Підтримка старих браузерів

Автоматична обробка JSON

XMLHttpRequest автоматично обробляє JSON, якщо встановити властивість responseType у значення "json". У цьому випадку відповідь від сервера буде автоматично розібрана як JSON і response буде містити об'єкт JavaScript.

var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://www.test.com/dani/test.json', true); xhr.responseType = 'json'; xhr.onload = function() { if(xhr.status >= 200 && xhr.status < 300){ console.log(this.response); // Автоматично розібраний JSON-об'єкт }else{ console.error('Помилка завантаження: ' + xhr.status); } }; xhr.onerror = function(){ console.error('Невдалий запит'); }; xhr.send();

fetch обробляє JSON через метод .json().

fetch('http://www.test.com/dani/test.json') .then(response => { if(!response.ok){ throw new Error('Помилка завантаження: ' + response.status); } return response.json(); // Розбирає JSON-дані }) .then(data => { console.log(data); // JSON-об'єкт }) .catch(error => { console.error('Невдалий запит: ', error); });

Скасування запиту

У XMLHttpRequest скасування запиту робиться за допомогою методу abort

var xhr = new XMLHttpRequest(); xhr.open('GET', '/ajax.php?act=test_info', true); xhr.onload = function(){ if(xhr.status >= 200 && xhr.status < 300){ console.log(this.response); }else{ console.error('Помилка завантаження: ' + xhr.status); } }; xhr.onerror=function() { console.error('Запит не вдався'); }; xhr.onabort=function(){ console.log('запит перервано'); } xhr.send(); //скасовуємо запит xhr.abort();

А для скасування запиту у fetch використовується AbortController.

// Створення контролера скасування const controller = new AbortController(); const signal = controller.signal; // Виконання fetch-запиту fetch('/ajax.php?act=test_info', { signal: signal }) .then(response => { if(!response.ok){ throw new Error('Мережна помилка: ' + response.status); } return response.text(); // Або response.json() залежно від типу очікуваних даних }) .then(data =>{ console.log(data); }) .catch(error => { if(error.name === 'AbortError'){ console.log('Запит перервано'); }else{ console.error('Помилка завантаження:', error); } }); // Скасування запиту controller.abort();

response streaming

XMLHttpRequest дозволяє обробляти дані фрагментами по мірі їх надходження за допомогою події onprogress.

var xhr = new XMLHttpRequest(); xhr.open("GET", "/ajax.php?act=stream", true); xhr.onprogress = function(event){ //фрагмент отриманої відповіді console.log("фрагмент: ", xhr.responseText); }; xhr.onload = function(){ if (xhr.status === 200){ //повна відповідь console.log("повна відповідь: ", xhr.responseText); }else{ console.error("Error: ", xhr.statusText); } }; xhr.send();

У fetch для обробки потокових даних використовується ReadableStream та TextDecoder. Функція read рекурсивно читає фрагменти відповіді до завершення потоку.

fetch("/ajax.php?act=stream").then(response =>{ if(!response.ok){ console.error("Error: ", response.statusText); return; } const reader = response.body.getReader(); const decoder = new TextDecoder(); let result = ""; reader.read().then(function processText({ done, value }){ if(done){ console.log("повна відповідь: ", result); // Завершальна обробка отриманого результату return; } const chunk = decoder.decode(value, { stream: true }); result+= chunk; console.log("фрагмент: ", chunk); // Обробка отриманого фрагмента return reader.read().then(processText); }); });

Прогрес завантаження файлу

У XmlHttpRequest за прогресом завантаження файлу (даних) можна слідкувати за допомогою події onprogress:

var xhr = new XMLHttpRequest(); xhr.open("GET", "/dani/test.ogg", true); xhr.responseType='blob'; xhr.onprogress = function(e){ if(e.lengthComputable){ var percentComplete = (e.loaded / e.total) * 100; document.getElementById("xhr_progres").innerHTML = "Процес завантаження: " + Math.round(percentComplete) + "% (" + e.loaded + " / " + e.total + " байт)"; }else{ document.getElementById("xhr_progres").innerHTML = "Процес завантаження: " + e.loaded + " байт"; } console.log(e.total, e.loaded); }; xhr.onload=function(){ if(xhr.status === 200){ document.getElementById("xhr_progres").innerHTML += "<br>Завантаження завершено!"; //отримання завантаженого файлу test.ogg const url = URL.createObjectURL(xhr.response); const a = document.createElement('a'); a.href = url; a.download = 'test.ogg'; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); }else{ document.getElementById("xhr_progres").innerHTML += "<br>Помилка завантаження!"; } }; xhr.onerror = function(){ document.getElementById("xhr_progres").innerHTML += "<br>Помилка мережі!"; }; xhr.send();

У fetch слідкування за прогресом завантаження реалізовується за допомогою ReadableStream, який дохволяє читиати отримані дані від сервера частинами:

fetch('/dani/test.ogg').then(response => { if(!response.ok){ document.getElementById("fetch_progres").innerHTML += "<br>Помилка мережі!"; return false; } const reader = response.body.getReader(); let contentLength = response.headers.get('Content-Length'); if (!contentLength){ contentLength='0'; } const total = parseInt(contentLength, 10); let loaded = 0; return new Response(new ReadableStream({ start(controller){ function push(){ reader.read().then(({ done, value }) => { if(done){ controller.close(); document.getElementById("fetch_progres").innerHTML += "<br>Завантаження завершено!"; return; } loaded += value.byteLength; if(total){ const percentComplete = (loaded / total) * 100; document.getElementById("fetch_progres").innerHTML = `Процес завантаження: ${Math.round(percentComplete)}% (${loaded} / ${total} байт)`; }else{ document.getElementById("fetch_progres").innerHTML = `Процес завантаження: ${loaded} байт`; } console.log(total, loaded); controller.enqueue(value); push(); }).catch(error =>{ console.error('Помилка при читанні потоку:', error); controller.error(error); }); } push(); } })); }).then(response => response.blob()).then(blob => { //отримання завантаженого файлу test.ogg const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'test.ogg'; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); }).catch(error => { document.getElementById("fetch_progres").innerHTML = "Помилка завантаження файлу."; console.error('Помилка завантаження файлу:', error); });

Висновок

Обидві технології мають свої плюси та мінуси. XmlHttpRequest має ширшу підтримку браузерами, але Fetch API надає більш зручний і сучасний спосіб роботи з HTTP-запитами. У більшості випадків, якщо вам не потрібно підтримувати старі браузери, використання Fetch API є більш переважним вибором завдяки його простоті та гнучкості.

Leo Bai 2024-08-04 16:08:15

Тільки зареєстровані користувачі можуть писати коментарі.