Для відправки 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 є більш переважним вибором завдяки його простоті та гнучкості.