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