Доброго времени суток, друзья!
При разработке веб-приложений часто возникает необходимость получения данных с сервера.
На сегодняшний день в JavaScript существует большое количество способов это сделать.
В статье будут рассмотрены следующие приемы:
- XMLHttpRequest
- JQuery.ajax
- Qwest
- SuperAgent
- Http-client
- Axios
- Fetch
Рассмотрим синтаксис каждого способа на примере простых GET и POST-запросов, остановимся на их плюсах и минусах.
В завершение решим парочку задач с использованием XMLHttpRequest, Axios и Fetch.
Большая часть теории является переводом этой статьи.
Итак, поехали.
XMLHttpRequest
Объект XMLHttpRequest — это старейший метод из рассматриваемых. Разумеется, другие методы превосходят его по функциональности. Однако он продолжает работать (в чем мы убедимся, когда перейдем к примерам) и может быть полезен для обеспечения обратной совместимости со старыми браузерами.
GET
let xhr = new XMLHttpRequest()
xhr.open('GET', 'https://example.com/users')
xhr.send()
xhr.onload = function() {
if (xhr.status != 200) {
console.log(`error ${xhr.status}: ${xhr.statusText}`)
} else {
// работаем с данными
console.log(xhr.response)
}
}
xhr.onerror = function() {
// обрабатываем ошибки
}
POST
let formData = new FormData()
formData.append('name', 'Harry')
let xhr = new XMLHttpRequest();
xhr.open('POST', 'https://example.com/users');
xhr.send(formData);
xhr.onload = () => console.log(xhr.response);
Преимущества:
- работает во всех браузерах
- является встроенным API браузера
- отсутствует необходимость загрузки из внешнего источника
- завершенность, стабильность
Недостатки:
- неуклюжий и многословный синтаксис
- может привести к «аду функций обратного вызова»
- заменяется на fetch при интерпретации
JQuery.ajax
Это широко используемая с давних пор библиотека для отправки асинхронных запросов.
Все методы ajax возвращают абстракцию (надстройку) над объектом XMLHttpRequest.
GET
$.ajax({
url: 'https://example.com/users'
}).done(function(data) {
// работаем с данными
console.log(data)
}).fail(function(error) {
// обрабатываем ошибки
console.log(error)
})
POST
$.ajax({
method: "POST",
url: "https://example.com/users",
data: { name: 'Harry', age: 29 }
})
.done(function(msg) {
console.log( 'data saved: ' + msg );
})
Преимущества:
- хорошая поддержка и документация
- объект запроса можно настраивать
- используется во многих проектах
- прост в изучении
- имеется возможность прерывания запроса
Недостатки:
- не является встроенным API браузера
- необходимо загружать из внешнего источника
- приходится добавлять весь функционал JQuery
Qwest
Qwest — это простая ajax-библиотека, основанная на промисах и поддерживающая тип данных XmlHttpRequest2, похожий на ArrayBuffer, Blob и FormData.
GET
qwest.get('https://example.com/users')
.then(function(xhr, response) {
// работаем с данными
console.log(response)
});
POST
qwest.post('https://example.com/users', {
name: 'Harry',
age: 29
})
.then(function(xhr, response) {
// работаем с данными
console.log(response)
})
.catch(function(e, xhr, response) {
// обрабатываем ошибки
console.log(e)
})
Преимущества:
- можно устанавливать ограничения на количество запросов
- основан на промисах
Недостатки:
- XmlHttpRequest2 доступен не во всех браузерах
- не является встроенным
- необходимо загружать из внешнего источника
Superagent
Superagent (visionmedia) — это простой в изучении ajax API, созданный для повышения гибкости и читаемости кода. Он также работает с node.js.
GET
const superagent = require('superagent')
superagent
.get('https://example.com/users')
.end((err, res) => {
// работаем с данными
console.log(res)
})
Метод query() принимает объекты — строки запросов.
superagent.get('https://example.com/users')
.query({
name: 'Harry'
})
.query({
age: 29
})
.end(response => {
console.log(response)
})
POST
superagent.post('https://example.com/users')
.send({
name: 'Harry'
})
.set('Accept', 'application/json')
.end(response => {
console.log(response)
})
Преимущества:
- основан на промисах
- работает как в браузере, так и в node.js
- для прерывания запроса вызывается метод request.abort()
- хорошо известная в сообществе разработчиков библиотека
- простой интерфейс
- поддержка повторных запросов
Недостатки:
- не поддерживается мониторинг процесса загрузки
- не является встроенным
- необходимо загружать из внешнего источника
Http-client
Http-client позволяет формировать клиент HTTP, используя Fetch API.
GET
// используем ES-6 модули
import {
createFetch,
base,
accept,
parse
} from 'http-client'
const fetch = createFetch(
base('https://example.com/users'),
accept('application.json'),
parse('json')
)
fetch('https://example.com/users').then(response => {
console.log(response.jsonData)
})
POST
import {
createFetch,
method,
params
} from 'http-client'
const fetch = createFetch(
params({
name: 'Harry'
}),
base('htpps://example.com/users')
)
Преимущества:
- работает как в браузере, так и в node.js
- используется сервис-воркерами
- основан на промисах
- обеспечивает хорошую защиту в политике общего происхождения
Недостатки:
- не является встроенным
- необходимо загружать из внешнего источника
Axios
Основанная на промисах библиотека для работы с запросами как в браузере, так и в node.js.
GET
const axios = require('axios');
axios.get('https://example.com/users')
.then(function(response) {
// работаем с данными
console.log(response);
})
.catch(function(error) {
// обрабатываем ошибки
console.log(error);
})
POST
axios.post('/user', {
name: 'Harry',
age: 29
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
Преимущества:
- использует промисы для решения проблемы «ада функций обратного вызова»
- работает как в браузере, так и в node.js
- поддерживается мониторинг процесса загрузки
- можно устанавливать задержку получения ответа
- имеется возможность настройки запросов
- реализована отмена промисов
- автоматическое преобразование данных в JSON
Недостатки:
- не является встроенным
- необходимо загружать из внешнего источника
Fetch
Fetch является встроенным API браузера, пришедшим на смену XMLHttpRequest. Он сильно упрощает работу с запросами. В основе fetch лежат промисы.
GET
fetch('https://example.com/users')
.then(response => response.json())
.then(result => console.log(result)))
POST
fetch('https://example.com/users', {
method: 'post',
headers: {
'Accept': 'application/json'
},
body: JSON.stringify({
name: 'Harry'
})
})
.then(res => res.json())
.then(res => console.log(res))
Преимущества:
- является встроенным
- не требуется загружать из внешнего источника
- основан на промисах
- не нуждается в определении зависимостей
- поддерживается всеми современными браузерами
- официальная замена XMLHttpRequest
- низкий порог вхождения
Недостатки:
- двухэтапный процесс: сначала мы делаем запрос, затем вызываем метод .json(). В Axios мы получаем ответ в формате JSON по умолчанию
- промис, возвращаемый Fetch(), отклоняется только в случае возникновения каких-либо проблем с сетью. Если ответом сервера будет 404 или 500, промис все равно выполняется
- не хватает полезного функционала других библиотек, например, возможности отменять запросы
- fetch по умолчанию не отправляет и не получает куки с сервера, что может привести к квалификации запроса как неаутентифицированного и его блокировке. Одним из способов решения данной проблемы является добавление { credentials: 'same-origin' } в объект запроса
Примеры
Давайте посмотрим, как XMLHttpRequest, Fetch и Axios используются на практике. В задачах будет использоваться JSONPlaceholder — фейковый онлайн REST API для тестирования и прототипирования (проще говоря, учебная база данных).
XMLHttpRequest
Задача: получить список пользователей и вывести этот список в консоль.
Решение:
// создаем объект XMLHttpRequest с помощью конструктора
let xhr = new XMLHttpRequest()
// открываем соединение
xhr.open('GET', 'https://jsonplaceholder.typicode.com/users')
// отправляем запрос
xhr.send()
/*xhr.onload = function() {
if (xhr.status != 200) {
console.log(`error ${xhr.status}:${xhr.statusText}`)
} else {
console.table(JSON.parse(xhr.response))
}
}*/
// сокращенный вариант
// если запрос провалился, выводим в консоль статус
// если выполнился, парсим ответ и выводим его в консоль
xhr.onload = () => xhr.status != 200 ? console.error(xhr.status) : console.table(JSON.parse(xhr.response))
// мониторим процесс загрузки
// если ответ содержит заголовок Content-Length, показываем процесс загрузки в байтах
// если не содержит, показываем общее количество загруженных байт
xhr.onprogress = event => {
if (event.lengthComputable) {
console.dir(`received ${event.loaded} of ${event.total} bytes`)
} else {
console.dir(`received ${event.loaded} bytes`)
}
}
// обрабатываем другие ошибки
xhr.onerror = () => console.error('error')
Результат:
Код на GitHub.
Fetch
Задача: аналогичная.
Решение (сравните с XMLHttpRequest):
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => console.table(json))
.catch(error => console.error(error))
Результат:
Код на GitHub.
Либо, если вам больше нравится async/await:
(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users')
const data = await response.json()
console.table(data)
} catch (error) {
console.error(error)
} finally {
console.log('done')
}
})()
Результат:
Код на GitHub.
Немного усложним задачу: теперь нужно получить с сервера изображения и сформировать из них галерею.
Решение:
const url = 'https://jsonplaceholder.typicode.com/photos/'
getPhotosAndMakeGallery(url)
function getPhotosAndMakeGallery(url) {
for (let i = 1; i < 11; i++) {
fetch(url + i)
.then(response => response.json())
.then(photo => {
let img = document.createElement('img')
img.src = photo.url
document.body.append(img)
})
}
}
Результат:
Код на GitHub.
Axios
Для подключения данной библиотеки необходимо добавить <script src=«unpkg.com/axios/dist/axios.min.js»></script> в head документа.
Задача: получить список пользователей и вывести их в консоль.
Решение:
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => console.table(response.data))
.catch(error => console.log(error))
Код на GitHub.
Либо, если вы предпочитаете async/await:
(async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
console.table(response.data);
} catch (error) {
console.error(error);
}
})()
Код на GitHub.
Усложняем задачу: получить todos и сформировать список.
Решение:
const url = 'https://jsonplaceholder.typicode.com/todos/'
getTodosAndMakeList()
function getTodosAndMakeList() {
const ul = document.createElement('ul')
document.body.append(ul)
for (let i = 1; i < 21; i++) {
axios.get(url + i)
.then(response => {
let li = document.createElement('li')
li.textContent = `title: ${response.data.title}; completed: ${response.data.completed}`
ul.append(li)
})
.catch(error => console.log(error))
}
}
Результат:
Код на GitHub.
Еще усложняем: получить пользователей, комментарии и фото, объединить эти данные и представить все в удобочитаемом виде.
Решение:
function getUsers(i) {
return axios.get('https://jsonplaceholder.typicode.com/users/' + i)
}
function getComments(i) {
return axios.get('https://jsonplaceholder.typicode.com/comments/' + i)
}
function getPhotos(i) {
return axios.get('https://jsonplaceholder.typicode.com/photos/' + i)
}
(async() => {
for (let i = 1; i < 7; i++) {
let request = await axios.all([getUsers(i), getComments(i), getPhotos(i)])
.then(axios.spread(function(user, comment, photo) {
let figure = document.createElement('figure')
document.body.append(figure)
let figcaption = document.createElement('figcaption')
figcaption.textContent = `Post ${i}`
figure.append(figcaption)
let img = document.createElement('img')
img.src = photo.data.url
figure.append(img)
let userName = document.createElement('p')
userName.innerHTML = `<span>Name:</span> ${user.data.username}`
figure.append(userName)
let userComment = document.createElement('p')
userComment.innerHTML = `<span>Comment:</span> ${comment.data.body}`
figure.append(userComment)
}))
}
})()
Результат:
Код на GitHub.
Благодарю за внимание.
Счастливого кодинга!
conopus
Удивительно, как у вас сходятся мысли с этим автором на Medium.
gohrytt
С каких пор перевод статьи это плохо? Лично мне гораздо приятнее и доступнее например читать на велком и могучем.
conopus
По-моему, я никого не осуждал. Более того, видно, что автор этого текста подошел к доработке творчески. У него и иллюстрации, и код на GitHub, чего нет в тексте, который раньше появился.
Но, хотелось бы узнать причину полного совпадения нескольких абзацев текста. И, если это заимствование у другого автора, то всегда стоит указывать первоисточник.
Eugeny1987
вероятно, с тех пора пока у статьи не стоит метка «перевод»