Март 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. «Асинхронный» определенно, но:
- JavaScript был вероятен, хотя VBScript и Flash были вариантами
- Полезная нагрузка не должна была быть 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, а некоторые параметры являются громоздкими. Используйте это с осторожностью в течение следующих нескольких лет.