Статьи

XMLHttpRequest против Fetch API: что лучше для Ajax в 2019 году?

Март 2019 года отмечает 20-летие Ajax. Вроде. Первая реализация XMLHttpRequest была выпущена в 1999 году как компонент ActiveX IE5.0 (не спрашивайте) .

До этого существовали способы извлечения данных с сервера без полного обновления страницы, но они часто полагались на неуклюжие методы, такие как внедрение <script> или сторонние плагины. Microsoft разработала первичный XMLHttpRequest для браузера на основе альтернативы своему почтовому клиенту Outlook.

XMLHttpRequest не был веб-стандартом до 2006 года, но он был реализован в большинстве браузеров. Его принятие в Gmail (2004) и Google Maps (2005) привело к публикации Джесси Джеймса Гарретта в 2005 году AJAX: Новый подход к веб-приложениям . Новый термин кристаллизуется в центре внимания разработчиков.

AJAX для Ajax

AJAX — это мнемоника асинхронного JavaScript и XML. «Асинхронный» определенно, но:

  1. JavaScript был вероятен, хотя VBScript и Flash были вариантами
  2. Полезная нагрузка не должна была быть XML, хотя это было популярно в то время. Можно использовать любой формат данных, и сегодня JSON обычно предпочтительнее.

Теперь мы используем «Ajax» в качестве общего термина для любого процесса на стороне клиента, который выбирает данные с сервера и динамически обновляет DOM без полного обновления страницы. Ajax — это основной метод для большинства веб-приложений и одностраничных приложений (SPA).

Extreme XMLHttpRequest

Следующий код JavaScript показывает базовый HTTP-запрос GET для http://domain/service с использованием XMLHttpRequest (обычно сокращается до XHR):

 let xhr = new XMLHttpRequest(); xhr.open('GET', 'http://domain/service'); // request state change event xhr.onreadystatechange = function() { // request completed? if (xhr.readyState !== 4) return; if (xhr.status === 200) { // request successful - show response console.log(xhr.responseText); } else { // request error console.log('HTTP error', xhr.status, xhr.statusText); } }; // start request xhr.send(); 

Объект XMLHttpRequest имеет много других параметров, событий и свойств ответа. Например, тайм-аут в миллисекундах может быть установлен и обнаружен:

 // set timeout xhr.timeout = 3000; // 3 seconds xhr.ontimeout = () => console.log('timeout', xhr.responseURL); 

и событие progress может сообщить о длительной загрузке файла:

 // upload progress xhr.upload.onprogress = p => { console.log( Math.round((p.loaded / p.total) * 100) + '%') ; } 

Количество параметров может вызывать недоумение, и ранние реализации XMLHttpRequest имели несколько кросс-браузерных несоответствий. По этой причине большинство библиотек и сред предлагают функции-оболочки Ajax для обработки сложности, например, метод jQuery.ajax() :

 // jQuery Ajax $.ajax('http://domain/service') .done(data => console.log(data)) .fail((xhr, status) => console.log('error:', status)); 

Быстрая перемотка вперед

Fetch API — это современная альтернатива XMLHttpRequest . Универсальные интерфейсы Headers , Request и Response обеспечивают согласованность, в то время как Promises позволяют проще создавать цепочки и выполнять асинхронное / ожидание без обратных вызовов. Приведенный выше пример XHR можно преобразовать в гораздо более простой код на основе Fetch, который даже анализирует возвращенный JSON:

 fetch( 'http://domain/service', { method: 'GET' } ) .then( response => response.json() ) .then( json => console.log(json) ) .catch( error => console.error('error:', error) ); 

Fetch чистый, элегантный, простой для понимания и интенсивно используемый в PWA Service Workers . Почему бы вам не использовать его вместо древнего XMLHttpRequest?

К сожалению, веб-разработка никогда не бывает такой четкой. Fetch еще не является полноценной заменой методам Ajax…

Поддержка браузера

Fetch API достаточно хорошо поддерживается , но он не будет работать во всех выпусках Internet Explorer. Люди, использующие версии Chrome, Firefox и Safari старше 2017 года, также могут испытывать проблемы. Эти пользователи могут составлять небольшую часть вашей пользовательской базы … или это может быть основной клиент. Всегда проверяйте, прежде чем начать кодирование!

Кроме того, API Fetch является более новым и получает больше текущих изменений, чем зрелый объект XHR. Эти обновления вряд ли повредят код, но ожидают некоторого технического обслуживания в ближайшие годы.

Cookieless по умолчанию

В отличие от XMLHttpRequest , не все реализации Fetch будут отправлять файлы cookie, поэтому аутентификация вашего приложения может завершиться неудачно. Проблема может быть исправлена ​​путем изменения параметров инициализации, передаваемых во втором аргументе, например

 fetch( 'http://domain/service', { method: 'GET', credentials: 'same-origin' } ) 

Ошибки не отклоняются

Удивительно, но ошибка HTTP, такая как 404 Page Not Found или 500 Internal Server Error , не приводит к отклонению Fetch Promise; .catch() никогда не запускается. Обычно он разрешается с состоянием response.ok установленным в false.

Отказ происходит только в том случае, если запрос не может быть выполнен, например, сбой сети. Это может усложнить реализацию перехвата ошибок.

Тайм-ауты не поддерживаются

Fetch не поддерживает тайм-ауты, и запрос будет продолжаться до тех пор, пока браузер выберет. Дополнительный код необходим, чтобы обернуть Выборку в другое Обещание, например

 // fetch with a timeout function fetchTimeout(url, init, timeout = 3000) { return new Promise((resolve, reject) => { fetch(url, init) .then(resolve) .catch(reject); setTimeout(reject, timeout); } } 

… Или, возможно, используйте Promise.race() к которому разрешается, когда сначала выполняется выборка или тайм-аут, например

 Promise.race([ fetch('http://url', { method: 'GET' }), new Promise(resolve => setTimeout(resolve, 3000)) ]) .then(response => console.log(response)) 

Отмена выборки

Запрос xhr.abort() легко завершить с помощью xhr.abort() и, при необходимости, обнаружить такое событие с помощью функции xhr.onabort .

Прекращение выборки было невозможно в течение нескольких лет, но теперь оно поддерживается в браузерах, которые реализуют API AbortController . Это запускает сигнал, который может быть передан объекту инициации Fetch:

 const controller = new AbortController(); fetch( 'http://domain/service', { method: 'GET' signal: controller.signal }) .then( response => response.json() ) .then( json => console.log(json) ) .catch( error => console.error('Error:', error) ); 

Выборка может быть прервана путем вызова controller.abort(); , Обещание отклоняет, поэтому .catch() функция .catch() .

Нет прогресса

На момент написания статьи Fetch не поддерживал события прогресса. Поэтому невозможно сообщить о статусе загрузки файлов или аналогичных представлений больших форм.

XMLHttpRequest против Fetch API?

В конечном счете, выбор за вами … если только у вашего приложения нет клиентов IE, которые требуют загрузки индикаторов выполнения .

Для простых вызовов Ajax XMLHttpRequest является более низким уровнем, более сложным, и вам потребуются функции-оболочки. К сожалению, то же самое произойдет и после того, как вы начнете учитывать сложности тайм-аутов, прерываний вызовов и отслеживания ошибок.

Вы можете выбрать полифил Fetch в сочетании с полифилом Promise, чтобы можно было писать код Fetch в IE. Тем не менее, XHR используется в качестве запасного варианта; не каждый параметр будет работать должным образом, например, файлы cookie будут отправляться независимо от настроек.

Принеси будущее. Тем не менее, API является относительно новым, он не обеспечивает все функциональные возможности XHR, а некоторые параметры являются громоздкими. Используйте это с осторожностью в течение следующих нескольких лет.