Полное руководство по XMLHttpRequest
Хватит искать и прочитай это
Прежде всего, давайте разберемся, о чем мы будем говорить.
Что это
Изобретен Microsoft в начале 90-х годов и сокращенно называется XHR, XMLHttpRequest. – это набор API-интерфейсов, которые могут использоваться языками сценариев веб-браузеров, такими как JavaScript, для передачи данных в и из Веб-сервер, использующий HTTP.
XHR можно использовать с протоколами, отличными от HTTP, и данные могут быть в форме не только XML, но и JSON, HTML или >простой текст.
Что вы можете сделать с XHR
Все современные браузеры имеют встроенный объект XMLHttpRequest для запроса данных с сервера. С объектом XHR вы можете:
- Обновить веб-страницу без перезагрузки страницы
- Запросить данные с сервера — после загрузки страницы.
- Получить данные с сервера — после загрузки страницы
- Отправить данные на сервер — в фоновом режиме
Что мы собираемся делать
Мы собираемся использовать поддельный онлайн REST API для тестирования и создания прототипов, например reqres (еще один хороший — JSONPlaceholder), он отлично подходит для учебных пособий, тестирования и примеров кода. В этом посте вы узнаете:
- как получать данные с помощью запросов GET и как отправлять с помощью POST
- Что такое callback и что такое promise (кратко)
- Улучшить наш код, чтобы сделать его более универсальным
- Обработка ошибок запроса
- Визуализируйте полученные данные
Получение данных
Нам нужно создать новый объект xhr, создав экземпляр XMLHttpRequest:
const xhr = new XMLHttpRequest();
это встроенная функция конструктора в вашем браузере, поэтому вам не нужно добавлять какую-либо специальную библиотеку или пакет.
На следующем шаге нам нужно подготовить HttpRequest к отправке, используя функцию, которая принимает два аргумента:
1) используемый HTTP-метод
2) URL-адрес, на который вы хотите отправить отправить этот запрос
xhr.open('GET', 'https:reqres.in/api/users')
Теперь мы можем отправить предварительно настроенный запрос:
xhr.send();
Теоретически это должно быть всем, что нам нужно сделать, фактически запрос отправляется на URL-адрес с использованием метода GET, и мы можем получить ответ, но мы еще не там. Нам нужно использовать ответ.
Небольшая преамбула
Есть два основных способа прослушивания события onload в нашем запросе xhr, а именно:
/*1*/ xhr.addEventListener("load", reqListener);
/*2*/ xhr.onload = () => { };
Мы собираемся использовать второй, так как он имеет более широкую поддержку браузеров.
Таким образом, функция onload сработает, когда мы получим ответ, чтобы получить данные, мы могли бы зарегистрировать ответ следующим образом:
xhr.onload = () => { console.log(xhr.response); };
в этом случае увидит в журнале консоли кучу данных, напечатанных в виде строки, но на самом деле это ответ JSON. Чтобы преобразовать этот ответ в объект Javascript, чтобы манипулировать им, мы можем его проанализировать:
xhr.onload = () => {
const data = JSON.parse(xhr.response);
console.log(data);
};
Другой способ проанализировать ответ JSON — установить для атрибута responseType значение «json»:
xhr.responseType = 'json';
xhr.onload = () => {
const data = xhr.response;
console.log(data);
};
Наш код должен выглядеть так:
const xhr = new XMLHttpRequest();
xhr.open('GET','https://reqres.in/api/users');
xhr.responseType = 'json';
xhr.onload = () => {
const data = xhr.response;
console.log(data);
};
xhr.send();
Отправка данных
Допустим, мы хотим создать нового пользователя, нам нужно будет отправить данные, чтобы указать имя и работу нового пользователя.
Единственное, что изменится, — это вызов xhr.open(), так как нам нужно изменить метод запроса на POST, установить URL-адрес для запроса и добавить данные для пользователя, которого мы хотим создать:
xhr.open('POST','https://reqres.in/api/users',{
"name":"morpheus",
"job":"leader"
});
reqres.in ответит на наш запрос со статусом 2xx и отправит нам дополнительную информацию об успешном создании пользователя с таким объектом ответа:
createdAt: "2020-02-03T17:10:31.110Z" id: "644"
Что такое обратный вызов
Проще говоря: обратный вызов — это функция, которая должна быть выполнена после завершения выполнения другой функции — отсюда и название «обратный вызов».
— Брэндон Морелли в этой прекрасной статье об обратных вызовах.
Что такое обещание
(Промисы изначально работают только в современных браузерах)
XHR на основе промисов может помочь вам избежать глубоко вложенных обратных вызовов и разрешить цепочку методов с использованием .then()
Вот как может выглядеть традиционная модель обратного вызова:
first(a,function(b){
second(b,function(c){
third(c,function(d){
fourth(d);
});
});
});
И как это может выглядеть при использовании подхода, основанного на промисах.
first(a)
.then(function(b){
return second(b);
})
.then(function(c){
return third(c);
})
.then(function(d){
return fourth(d);
})
.catch(function (error){
// error at any point of the chain
});
довольно яснее, а?
Давайте улучшим код
Хорошо, теперь, когда мы поняли основы, пришло время сделать вещи более профессиональными, создав функцию для получения/отправки данных через HttpRequests:
const sendHttpRequest = (method, url) => {
const xhr = new XMLHttpRequest();
xhr.open(method,url);
xhr.responseType = 'json';
xhr.onload = () => {
const data = xhr.response;
console.log(data);
};
xhr.send();
};
Еще не там. Давайте добавим обещания.
const sendHttpRequest = (method, url) => {
//Adding promise
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method,url);
xhr.responseType = 'json';
xhr.onload = () => {
resolve(xhr.response);
};
xhr.send();
});
//returning the promise
return promise
};
//Using .then() to retrieve data
const getData = () => {
sendHttpRequest('GET','https://reqres.in/api/users')
.then(responseData => {
console.log(responseData);
});
};
Мы также можем отправлять POST-запросы, но необходимо внести некоторые изменения в sendHttpRequest:
//adding data parameter
const sendHttpRequest = (method, url, data) => {
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method,url);
xhr.responseType = 'json';
//signals that we are appending json data in POST
if (data){
xhr.setRequestHeader('Content-Type','application/json');
}
xhr.onload = () => {
resolve(xhr.response);
};
//appending json data
xhr.send(JSON.stringify(data));
});
return promise
};
//Sending POST request
const sendData = () => {
sendHttpRequest('POST','https://reqres.in/api/users',{
name: "morpheus",
job: "leader"
}).then(responseData => {
console.log(responseData);
})
};
Отлично, теперь мы можем вызывать getData() и sendData(), чтобы протестировать их:

Обработка ошибок
Все идет нормально. Но как насчет ошибок?
Прежде всего, прежде чем обрабатывать ошибки xhr, нам нужно понять небольшую разницу: есть два основных типа ошибок:
- Ошибки ответа
Не удалось ответить из-за сетевого подключения или неверного URL-адреса. - Ошибки состояния
На самом деле мы можем получить ответ от сервера, но со статусом ошибки (например, 400); это потому, что сервер не распознал наш запрос (например, мы пропустили параметр в запросе POST).
Давайте изменим наш код для обработки этих ошибок:
const sendHttpRequest = (method, url, data) => {
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method,url);
xhr.responseType = 'json';
if (data){
xhr.setRequestHeader('Content-Type','application/json');
}
xhr.onload = () => {
//rejecting promise on status error
if(xhr.status >= 400) {
reject(xhr.response);
} else {
resolve(xhr.response);
}
};
//rejecting promise on response errors
xhr.onerror = () => {
reject('Something went wrong!');
};
xhr.send(JSON.stringify(data));
});
return promise
};
const sendData = () => {
sendHttpRequest('POST','https://reqres.in/api/users',{
name: "morpheus",
job: "leader"
}).then(responseData => {
console.log(responseData);
//catching and printing errors
}).catch(err => {
console.log(err);
});
};
Теперь, если мы отключим интернет-соединение и попытаемся вызвать sendData(), мы получим в журнале something went wrong!, то же самое, если мы попытаемся достичь недопустимого URL-адреса.
В зависимости от запроса POST, если мы попытаемся опустить поле в отправляемых данных:
const sendData = () => {
sendHttpRequest('POST', 'https://reqres.in/api/register', {
email: '[email protected]'
// password: 'pistol'
}).then(responseData => {
console.log(responseData);
}).catch(err => {
console.log(err);
});
};
мы получим ошибку статуса error: "Missing email or username"
Визуализируйте полученные данные
Вы можете просто визуализировать данные, используя функцию, которая добавляет элементы DOM.
// Visualize in HTML the data from GET requests
const visualize = (data) => {
// Referring to an existing element in HTML code
// (in this case <div id="data-from-xhr"></div>)
let div = document.getElementById('data-from-xhr')
// Clear div element
div.innerHTML = ''
// For every data element:
data.map(userData => {
// Create DOM elements
let user = document.createElement('div')
let avatar = document.createElement('img')
let email = document.createElement('p')
let fullName = document.createElement('p')
// Fill DOM elements with passed data
avatar.src=userData.avatar
email.innerText = userData.email
fullName.innerText = userData.first_name + userData.last_name
// Create a user node each one with avatar, email and fullName
user.appendChild(avatar)
user.appendChild(email)
user.appendChild(fullName)
// Append the user node to div in HTML
div.appendChild(user)
})
};
с некоторыми css это должно выглядеть так:

Немного конфет для вас
Состояния запроса:
мы можем отслеживать состояние запроса, используя событие onreadystatechange:
xhr.onreadystatechange = function () {
if(xhr.readyState == 1) {
console.log('Request started.');
}
if(xhr.readyState == 2) {
console.log('Headers received.');
}
if (xhr.readyState == 3) {
console.log('Data loading..!');
}
if (xhr.readyState == 4) {
console.log('Request ended.');
}
};
Прервать запрос
прервать запрос XHR в любое время, вызвав метод abort():
xhr.abort(); // cancel request
Событие выполнения
Запускается периодически во время загрузки ответов и может использоваться для отчета о ходе выполнения тяжелых сетевых запросов.
xhr.onprogress = (event) => {
// event.loaded returns how many bytes are downloaded
// event.total returns the total number of bytes
// event.total only if server sends `Content-Length` header
console.log(`Downloaded ${event.loaded} of ${event.total}`);
}
Хотите реальный пример кода?
Вот мой репозиторий на github о XHR.
использованная литература
youtube.com: Отправка HTTP-запросов Javascript с помощью XMLHttpRequest
codeburst.io: JavaScript: что такое обратный вызов?
gomakethings.com : XHR на основе обещаний
attacomsian.com: Выполнение HTTP-запросов с использованием XMLHttpRequest (XHR)
w3schools. com: XML HttpRequest
developer.mozilla.org: XMLHttpRequest
wikipedia.org: XMLHttpRequest