Fetch API появился относительно недавно в спецификации ES6 (EcmaScript2015), но при этом успешно поддерживается практически всеми основными браузерами, за исключением Internet Explorer v.6 – 11 и Opera Mini по данным сайта caniuse.com. На этом сайте мы видим пояснение, что Fetch API – это современная замена XMLHttpRequest (A modern replacement for XMLHttpRequest
), то есть AJAX-технологии.
Общий обзор и синтаксис
Fetch
Функция fetch()
является методом объекта window
в JavaScript и составляющей Fetch API. Этот метод встроен в ядро современного JS, поэтому пользователям не нужно устанавливать никакого дополнительного кода. Fetch()
позволяет нам получать данные из API асинхронно без установки дополнительных библиотек.
1
2
3
4
5
6
7
|
fetch(url)
.then((res) => {
// код ответа
})
.catch((error) => {
// код ошибки
});
|
Приведенный выше фрагмент кода представляет собой простой запрос на получение с помощью функции fetch()
. В методе fetch()
есть один обязательный аргумент – url
. Параметр url
– это адрес, с которого пользователь хотел бы получать данные. Метод fetch()
возвращает промис (Promise, или обещание), который может разрешиться в виде объекта ответа или отклонить его с ошибкой.
1
2
3
4
5
6
7
8
9
|
fetch(url, {
method: “POST”,
headers: {
“Content-Type”: “application/json”,
},
body: JSON.stringify(data),
})
.then((response) => response.json())
.catch((error) => console.log(error));
|
Вторые аргументы в методе fetch()
– это параметры, и они не являются обязательными. Если пользователь не передает параметры, запрос всегда получает и загружает контент с заданного URL. Промис возвращает объект ответа, и поэтому пользователям необходимо использовать другой метод для получения тела ответа. Есть несколько различных методов, которые пользователи могут использовать в зависимости от формата тела ответа:
response.json()
response.text()
response.blob()
response.formData()
response.arrayBuffer()
Самым популярным является response.json()
.
К сожалению, встроенной функции fetch()
нет в Node.js, но есть полифил, такой как node-fetch. Существует несколько известных вариаций между node-fetch и fetch()
браузера.
Axios
Axios – это библиотека JavaScript для выполнения HTTP-запросов из Node.js, XMLHttpRequest или браузера. Как современная библиотека, она основана на Promise API, Axios имеет некоторые преимущества, такие как защита от атак с подделкой межсайтовых запросов (CSFR). Чтобы иметь возможность использовать библиотеку Axios, пользователи должны установить ее и импортировать в ваш проект, используя CDN, npm, Yarn или Bower.
1
2
3
|
axios.get(url)
.then((response) => console.log(response))
.catch((error) => console.log(error));
|
Приведенный выше фрагмент кода представляет собой использование метода get()
для получения и обработки как ответа от сервера, так и ошибки. Когда пользователи создают объект конфигурации, они могут определять набор свойств. Наиболее распространены такие, как url
, baseURL
, params
, auth
, headers
, responseType
и data
. В качестве ответа Axios возвращает промис, который разрешится с помощью объекта ответа или объекта ошибки. В объекте ответа есть следующие значения:
data
: фактическое тело ответаstatus
: код состояния HTTP вызова, например 200 или 404statusText
: статус HTTP в виде текстового сообщенияheaders
: заголовки – то же, что и в запросеconfig
: запрос конфигурацииrequest
: объект XMLHttpRequest (XHR)
1
2
3
4
5
6
7
8
|
axios({
url: “http://api.com”,
method: “POST”,
header: {
“Content-Type”: “application/json”,
},
data: { name: “Sabesan”, age: 25 },
});
|
В fetch()
пользователям необходимо работать с двумя обещаниями . В Axios они могут избегать шаблонов и писать более чистый и лаконичный код. Давайте посмотрим на отличия.
Axios использует свойство data
для работы с данными, а fetch()
– свойство body
.
Данные в fetch()
преобразованы в строку.
URL-адрес в fetch()
передается как аргумент, а в Axios URL-адрес устанавливается в объекте конфигурации.
Использование JSON
Fetch
После применения метода fetch()
, пользователям необходимо использовать какой-то метод для обработки данных ответа. Кроме того, когда пользователи отправляют тело с запросом, пользователям необходимо преобразовать данные в строку.
1
2
3
4
|
fetch(‘url’)
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.log(error));
|
В приведенном выше фрагменте кода с ответом пользователям необходимо использовать response.json()
, а затем написать обработку ответа, в который поступают данные. То есть процесс всегда будет двухэтапным.
Axios
В Axios пользователи передают данные в запросе или получают данные из ответа, причем данные автоматически преобразовываются в строку. Следовательно, никаких других операций не требуется.
1
2
3
|
axios.get(‘url’)
.then((response)=>console.log(response))
.catch((error)=>console.log(error))
|
В приведенном выше примере вы видите, что вам нужен всего один then
.
Автоматическое преобразование данных – отличная функция и плюс в копилку Axios.
Обработка ошибок (Error Handling)
Fetch
Каждый раз, когда вы получаете ответ от метода fetch()
, вам нужно проверять, является ли статус успешным, потому что даже если это не так, вы все равно получите ответ. В случае fetch()
обещание не будет выполнено тогда и только тогда, когда не будет выполнен сам запрос.
1
2
3
4
5
6
7
8
9
|
fetch(‘url’)
.then((response)=>{
if(!response.ok){
throw Error (response.statusText);
}
return response.json();
})
.then((data)=>console.log(data))
.catch((error)=>console.log(error))<code class=“jm kt ku kv kw b”>
|
Fetch()
не вызывает сетевых ошибок. Следовательно, вы всегда должны проверять свойство response.ok
при работе с fetch()
. Вы можете вынести проверку ошибок в функцию, чтобы сделать ее проще и удобнее для повторного использования.
1
2
3
4
5
6
7
8
9
|
const checkError = response => {
if (!response.ok) throw Error(response.statusText);
return response.json();
};
fetch(“url”)
.then(checkError)
.then(data => console.log(data))
.catch(error => console.log(“error”, error));
|
Axios
В Axios обрабатывать ошибки довольно просто, потому что Axios выдает сетевые ошибки. Если будет неподходящий ответ, например с кодом 404, промис будет отклонен и вернет ошибку. Следовательно, вам нужно отловить ошибку, и вы можете проверить, что это была за ошибка.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
axios.get(‘url’)
.then((response)=> console.log(response))
.catch((error)=>{
if(error.response){
// Когда код состояния ответа выходит за пределы диапазона 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request){
//Когда не был получен ответ после того, как запрос был сделан
console.log(error.request);
} else {
// Ошибка
console.log(error.message);
}
})
|
Прогресс загрузки внешних файлов (download progress)
При загрузке больших активов индикаторы прогресса очень полезны для пользователей с низкой скоростью интернета. Раньше для создания индикаторов прогресса загрузки разработчики использовали XMLHttpRequest.onprogress
в качестве обработчика обратного вызова.
Fetch
Чтобы отслеживать прогресс загрузки в fetch()
, вы можете использовать одно из свойств response.body
, объект ReadableStream
. Он предоставляет фрагменты основных данных и позволяет подсчитать, сколько данных потребляется во времени.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
// original code: https://github.com/AnthumChris/fetch-progress-indicators
const element = document.getElementById(‘progress’);
fetch(‘url’)
.then(response => {
if (!response.ok) {
throw Error(response.status+‘ ‘+response.statusText)
}
// убеждаемся, что ReadableStream поддерживается браузером
if (!response.body) {
throw Error(‘ReadableStream пока еще не поддерживается вашим браузером.’)
}
// сохраняем размер тела объекта в байтах
const contentLength = response.headers.get(‘content-length’);
// убеждаемся, что contentLength доступен
if (!contentLength) {
throw Error(‘Заголовок ответа Content-Length не доступен’);
}
// преобразовываем целое число в число с основанием 10
const total = parseInt(contentLength, 10);
let loaded = 0;
return new Response(
// создаем и возвращаем объект ReadableStream
new ReadableStream({
start(controller) {
const reader = response.body.getReader();
read();
function read() {
reader.read().then(({done, value}) => {
if (done) {
controller.close();
return;
}
loaded += value.byteLength;
progress({loaded, total})
controller.enqueue(value);
read();
}).catch(error => {
console.error(error);
controller.error(error)
})
}
}
})
);
})
.then(response =>
// создать blob из данных
response.blob()
)
.then(data => {
// вставляем загруженное изображение на страницу
document.getElementById(‘img’).src = URL.createObjectURL(data);
})
.catch(error => {
console.error(error);
})
function progress({loaded, total}) {
element.innerHTML = Math.round(loaded/total*100)+‘%’;
}
|
В приведенном выше примере демонстрируется использование ReadableStream
для предоставления пользователям прогресса загрузки изображений.
Axios
В Axios также возможна реализация индикатора прогресса, и это даже проще, потому что существует готовый модуль, который можно установить и внедрить. Он называется Axios Progress Bar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
loadProgressBar();
function downloadFile(url) {
axios.get(url, {responseType: ‘blob’})
.then(response => {
const reader = new window.FileReader();
reader.readAsDataURL(response.data);
reader.onload = () => {
document.getElementById(‘img’).setAttribute(‘src’, reader.result);
}
})
.catch(error => {
console.log(error)
});
}
|
Прогресс загрузки файлов на сервер (Upload Progress)
Fetch
Используя fetch()
, вы не можете отслеживать процесс загрузки каких-либо файлов на сервер.
Axios
В Axios, напротив, вы можете следить за процессом загрузки файлов, которые находятся на вашем компьютере или сервере.
1
2
3
4
5
|
const config = {
onUploadProgress: event => console.log(event.loaded)
};
axios.put(“/api”, data, config);
|
HTTP-перехват
Перехват может быть важен для вас, когда вам нужно проверить или изменить свой HTTP-запрос от приложения к серверу или наоборот – например, аутентификация, ведение журнала и т. д.
Fetch
Fetch API по умолчанию не обеспечивает перехват HTTP. Существует возможность перезаписать метод fetch()
и определить, что должно произойти во время отправки запроса, но это займет больше времени и строчек кода, да и может быть сложнее, чем использование функций Axios. Вы можете перезаписать глобальный метод fetch()
и определить свой собственный перехватчик, как в следующем коде:
1
2
3
4
5
6
7
8
9
10
11
12
|
fetch = (originalFetch => {
return (...arguments) => {
const result = originalFetch.apply(this, arguments);
return result.then(console.log(‘Request was sent’));
};
})(fetch);
fetch(‘url’)
.then(response => response.json())
.then(data => {
console.log(data)
});
|
Axios
HTTP-перехват Axios – одна из ключевых особенностей этой библиотеки, поэтому вам не нужно создавать дополнительный код для ее использования.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// перехватчики запросов
axios.interceptors.request.use((config)=>{
console.log(‘Запрос отправлен’);
return config;
})
// перехватчики запросов
axios.interceptors.response.use((response) => {
// выполнить операцию в зависимости от ответа
return response;
})
axios.get(‘url’)
.then((response)=>console.log(response))
.catch((error)=>console.log(error))
|
В приведенном выше коде методы axios.interceptors.request.use()
и axios.interceptors.response.use()
используются для определения кода, который будет запускаться перед отправкой HTTP-запроса.
Тайм-аут ответа
Fetch
Fetch()
обеспечивает функцию тайм-аута ответа через интерфейс AbortController
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
const controller = new AbortController();
const signal = controller.signal;
const options = {
method: ‘POST’,
signal: signal,
body: JSON.stringify({
firstName: ‘Sabesan’,
lastName: ‘Sathananthan’
})
};
const promise = fetch(‘/login’, options);
const timeoutId = setTimeout(() => controller.abort(), 5000);
promise
.then(response => {/* handle the response */})
.catch(error => console.error(‘timeout exceeded’));
|
В приведенном выше коде с помощью конструктора AbortController.AbortController()
необходимо создать объект AbortController
. Объект AbortController
позволяет позже прервать запрос. В статье «Глубокое понимание API Fetch JavaScript» вы найдете информацию о том, как signal
является свойством AbortController
, который доступен только для чтения. signal
предоставляет способ связаться с запросом или прервать его. Если сервер не отвечает менее чем через пять секунд, операция завершается вызовом controller.abort()
.
Axios
Используя необязательное свойство тайм-аута в объекте конфигурации, вы можете установить количество миллисекунд до завершения запроса.
1
2
3
4
5
6
7
8
9
10
11
|
axios({
method: ‘post’,
url: ‘/login’,
timeout: 5000, // 5 секунд тайм-аута
data: {
firstName: ‘Sabesan’,
lastName: ‘Sathananthan’
}
})
.then(response => {/* заголовок ответа */})
.catch(error => console.error(‘timeout exceeded’))
|
Одна из причин, по которой разработчики JavaScript выбирают Axios, а не fetch()
, – простота установки тайм-аута.
Параллельные запросы
Fetch
Чтобы сделать несколько одновременных запросов, вы можете использовать встроенный метод Promise.all()
. Просто передайте массив запросов fetch()
в Promise.all()
, а затем асинхронную функцию для обработки ответа.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Promise.all([
fetch(‘https://api.github.com/users/sabesansathananthan’),
fetch(‘https://api.github.com/users/rcvaram’)
])
.then(async([res1, res2]) => {
const a = await res1.json();
const b = await res2.json();
console.log(a.login + ‘ has ‘ + a.public_repos + ‘ public repos on GitHub’);
console.log(b.login + ‘ has ‘ + b.public_repos + ‘ public repos on GitHub’);
})
.catch(error => {
console.log(error);
});
|
Axios
Вы можете добиться указанного выше результата, используя метод axios.all()
. Передайте все запросы на выборку в виде массива методу axios.all()
. Назначьте свойства массива ответов отдельным переменным с помощью функции axios.spread()
, например:
1
2
3
4
5
6
7
8
9
|
axios.all([
axios.get(‘https://api.github.com/users/sabesansathananthan’),
axios.get(‘https://api.github.com/users/rcvaram’)
])
.then(axios.spread((obj1, obj2) => {
// Оба запросв теперь завершены
console.log(obj1.data.login + ‘ has ‘ + obj1.data.public_repos + ‘ public repos on GitHub’);
console.log(obj2.data.login + ‘ has ‘ + obj2.data.public_repos + ‘ public repos on GitHub’);
}));
|
Обратная совместимость
Обратная совместимость, или поддержка браузерами, может быть важна, если вы пишите код, который должен хорошо работать не только в современных браузерах, но и в устаревших или малопопулярных тоже.
Fetch
Fetch API поддерживают браузеры Chrome 42+, Safari 10.1+, Firefox 39+ и Edge 14+. Полная совместимая таблица доступна на сайте caniuse.com. Чтобы реализовать функции, аналогичные fetch()
в браузерах, которые не поддерживают этот метод, вы можете использовать fetch()
с полифиллом, таким как windows.fetch()
.
Чтобы использовать полифилл fetch, установите его с помощью этой команды npm:
1
2
3
4
|
import {fetch as fetchPolyfill} from ‘whatwg-fetch’
window.fetch(...) // use native browser version
fetchPolyfill(...) // use polyfill implementation
|
Имейте в виду, что в некоторых старых браузерах вам может также понадобиться полифилл для промисов.
Axios
Axios не похож на fetch()
. Axios обеспечивает широкую поддержку браузеров. Даже старые браузеры, такие как IE11, могут без проблем запускать Axios. Полная таблица совместимости доступна в документации Axios.
Заключение
Для большинства ваших нужд в HTTP-соединениях Axios предоставляет простой в использовании API в компактном виде. Есть несколько альтернативных библиотек для связи по протоколу HTTP, например ky, крошечный и элегантный HTTP-клиент, основанный на window.fetch
, или superagent – небольшая прогрессивная клиентская библиотека HTTP-запросов, основанная на XMLHttpRequest.
Однако, Axios – это лучшее решение для приложений с большим количеством HTTP-запросов и для тех разработчиков, которым нужна хорошая обработка ошибок или перехват HTTP-запросов. В случае же небольших проектов с несколькими простыми вызовами Fetch API может быть лучшим выбором.
На основе статьи Why JavaScript Developers Should Prefer Axios Over Fetch