Ajax
Что такое AJAX?
AJAX — это аббревиатура, которая означает Asynchronous Javascript and XML. На самом деле, AJAX не является новой технологией, так как и Javascript, и XML существуют уже довольно продолжительное время, а AJAX — это синтез обозначенных технологий. AJAX чаще всего ассоциируется с термином Web 2.0 и преподносится как новейшее Web-приложение.
При использовании AJAX нет необходимости обновлять каждый раз всю страницу, так как обновляется только ее конкретная часть. Это намного удобнее, так как не приходится долго ждать, и экономичнее, так как не все обладают безлимитным интернетом. Правда в этом случае, разработчику необходимо следить, чтобы пользователь был в курсе того, что происходит на странице. Это можно реализовать с использованием индикаторов загрузки, текстовых сообщений о том, что идёт обмен данными с сервером. Необходимо также понимать, что не все браузеры поддерживают AJAX (старые версии браузеров и текстовые браузеры). Плюс Javascript может быть отключен пользователем. Поэтому, не следует злоупотреблять использованием технологии и прибегать к альтернативным методам представления информации на Web-сайте.
Обобщим достоинства AJAX:
- Возможность создания удобного Web-интерфейса
- Активное взаимодействие с пользователем
- Частичная перезагрузка страницы, вместо полной
- Удобство использования
AJAX использует два метода работы с веб-страницей: изменение Web-страницы не перезагружая её, и динамическое обращение к серверу. Второе может осуществляться несколькими способами, в частности, XMLHttpRequest, о чем мы и будем говорить, и использование техники скрытого фрейма.
Что можно делать с помощью AJAX?
Элементы интерфейса
В первую очередь AJAX полезен для форм и кнопок, связанных с элементарными действиями: добавить в корзину, подписаться, и т.п.
Сейчас – в порядке вещей, что такие действия на сайтах осуществляются без перезагрузки страницы.
Динамическая подгрузка данных
Например, дерево, которое при раскрытии узла запрашивает данные у сервера.
Живой поиск
Живой поиск – классический пример использования AJAX, взятый на вооружение современными поисковыми системами.
Пользователь начинает печатать поисковую фразу, а JavaScript предлагает возможные варианты, получая список самых вероятных дополнений с сервера.
Код, который это обеспечивает, работает следующим образом.
Код активируется примерно при каждом нажатии клавиши, но не чаще чем раз в 100 мс (примерно). 2. Создаётся скрытый DIV и заполняется ответом сервера:
- Текущий результат подсвечен, можно перемещаться и выбирать
- При нажатии правой стрелки или при клике -- поиск в подрезультатах
- Результаты запросов кешируются, повторных обращений к серверу не происходит.
- В Google не только предлагаются варианты, но система тут же инициирует и сам поиск, т.е. не нужно даже нажимать
key:Enter
. Технически, с помощью AJAX можно обмениваться любыми данными с сервером.
Обычно используются форматы:
JSON – для отправки и получения структурированных данных, объектов. XML – если сервер почему-то работает в формате XML, то можно использовать и его, есть средства. HTML/текст – можно и просто загрузить с сервера код HTML или текст для показа на странице. Бинарные данные, файлы – гораздо реже, в современных браузерах есть удобные средства для них.
XMLHttpRequest
XMLHttpRequest
– это встроенный в браузер объект, который даёт возможность делать HTTP-запросы к серверу без перезагрузки страницы.
Несмотря на наличие слова «XML» в названии, XMLHttpRequest может работать с любыми данными, а не только с XML. Мы можем загружать/скачивать файлы, отслеживать прогресс и многое другое.
Важно
На сегодня не обязательно использовать XMLHttpRequest
, так как существует другой, более современный метод fetch
.
В современной веб-разработке XMLHttpRequest
используется по трём причинам:
По историческим причинам: существует много кода, использующего XMLHttpRequest
, который нужно поддерживать.
- Необходимость поддерживать старые браузеры, и нежелание использовать полифилы (например, чтобы уменьшить количество кода).
- Потребность в функциональности, которую fetch пока что не может предоставить, к примеру, отслеживание прогресса отправки на сервер.
- Что-то из этого списка звучит знакомо? Если да, тогда вперёд, приятного знакомства с XMLHttpRequest. Если же нет, возможно, имеет смысл изучать сразу Fetch.
XMLHttpRequest
- имеет два режима работы: синхронный и асинхронный.
Асинхронный режим:
Чтобы сделать запрос, нам нужно выполнить следующее:
1// 1. Создаем экземпляр XMLHttpRequest.2let xhr = new XMLHttpRequest();3// 2. Инициализировать его.4xhr.open(method, URL, [async, user, password])5// 3. Шлем запрос6xhr.send([body])78// 4. Слушать события на xhr, чтобы получить ответ.910xhr.onload = function() {11 alert(`Загружено: ${xhr.status} ${xhr.response}`);12};1314// происходит, только когда запрос совсем не получилось выполнить15xhr.onerror = function() {16 alert(`Ошибка соединения`);17};18// запускается периодически19xhr.onprogress = function(event) {20 // event.loaded - количество загруженных байт21 // event.lengthComputable = равно true, если сервер присылает заголовок Content-Length22 // event.total - количество байт всего (только если lengthComputable равно true)23 alert(`Загружено ${event.loaded} из ${event.total}`);24};
xhr.open
Метод обычно вызывается сразу после new XMLHttpRequest. В него передаются основные параметры запроса:
method
– HTTP-метод. Обычно это "GET" или "POST".URL
– URL, куда отправляется запрос: строка, может быть и объектURL
.async
– если указатьfalse
, тогда запрос будет выполнен синхронно.user
,password
– логин и пароль для базовой HTTP-авторизации (если требуется).
Примечание
Вызов open
, вопреки своему названию, не открывает соединение. Он лишь конфигурирует запрос, но непосредственно отсылается запрос только лишь после вызова send
.
xhr.send([body])
Метод устанавливает соединение и отсылает запрос к серверу. Необязательный параметр body
содержит тело запроса.
Некоторые типы запросов, такие как GET
, не имеют тела.
А некоторые, как, например, POST
, используют body
, чтобы отправлять данные на сервер.
Три наиболее используемых события xhr
:
load
– происходит, когда получен какой-либо ответ, включая ответы с HTTP-ошибкой, например404
.error
– когда запрос не может быть выполнен, например, нет соединения или невалидный URL.progress
– происходит периодически во время загрузки ответа, сообщает о прогрессе.
Полный пример получения списка стран с помощью XMLHttpRequest
После ответа сервера мы можем получить результат запроса в следующих свойствах xhr
:
status
- Код состояния HTTP
(число): 200
, 404
, 403
и так далее, может быть 0
в случае, если ошибка не связана с HTTP
.
statusText
- Сообщение о состоянии ответа HTTP
(строка): обычно OK
для 200
, Not Found
для 404
, Forbidden
для 403
, и так далее.
response
- в старом коде может встречаться как responseText
Тип ответа
Мы можем использовать свойство xhr.responseType
, чтобы указать ожидаемый тип ответа:
- "" (по умолчанию) – строка,
- "text" – строка,
- "arraybuffer" – ArrayBuffer (для бинарных данных, смотрите в ArrayBuffer, бинарные массивы),
- "blob" – Blob (для бинарных данных, смотрите в Blob),
- "document" – XML-документ (может использовать XPath и другие XML-методы),
- "json" – JSON (парсится автоматически).
1//Создаём новый XMLHttpRequest-объект2 const xhr = new XMLHttpRequest();34 //Настраиваем его: GET-запрос по URL https://restcountries.eu/rest/v2/all5 xhr.open("GET", "https://restcountries.eu/rest/v2/all");67 // Отсылаем запрос8 xhr.send();910 xhr.responseType = "json";1112xhr.onload = function() {13 const countires = xhr.response;14 alert(countires.length); // выведет количество стран15};
Состояния запроса
У XMLHttpRequest
есть состояния, которые меняются по мере выполнения запроса. Текущее состояние можно посмотреть в свойстве xhr.readyState
.
Список всех состояний, указанных в спецификации:
1UNSENT = 0; // исходное состояние2OPENED = 1; // вызван метод open3HEADERS_RECEIVED = 2; // получены заголовки ответа4LOADING = 3; // ответ в процессе передачи (данные частично получены)5DONE = 4; // запрос завершён
Состояния объекта XMLHttpRequest меняются в таком порядке: 0 → 1 → 2 → 3 → … → 3 → 4. Состояние 3 повторяется каждый раз, когда получена часть данных.
Изменения в состоянии объекта запроса генерируют событие readystatechange:
1xhr.onreadystatechange = function() {2 if (xhr.readyState === 3) {3 // загрузка4 }5 if (xhr.readyState === 4) {6 // запрос завершён7 }8};
Отмена запроса
Если мы передумали делать запрос, можно отменить его вызовом xhr.abort():
xhr.abort()
; // завершить запрос
При этом генерируется событие abort, а xhr.status устанавливается в 0.
Синхронные запросы
Если в методе open
третий параметр async
установлен на false
, запрос выполняется синхронно.
Другими словами, выполнение JavaScript останавливается на send()
и возобновляется после получения ответа. Так ведут себя, например, функции alert
или prompt
.
Синхронный код на получение списка стран с сервера:
1const xhr = new XMLHttpRequest();2 xhr.open("GET", "https://restcountries.eu/rest/v2/all",false);3try {4 xhr.send();5 if (xhr.status !== 200) {6 alert(`Ошибка ${xhr.status}: ${xhr.statusText}`);7 } else {8 alert(xhr.response);9 }10} catch(err) { // для отлова ошибок используем конструкцию try...catch вместо onerror11 alert("Запрос не удался");12}
Важно
Синхронные запросы используются редко, так как они блокируют выполнение JavaScript до тех пор, пока загрузка не завершена. В некоторых браузерах нельзя прокручивать страницу, пока идёт синхронный запрос.
Fetch
Метод fetch() — современный и очень мощный, поэтому начнём с него. Он не поддерживается старыми (можно использовать полифил), но поддерживается всеми современными браузерами.
Базовый синтаксис:
1let promise = fetch(url, [options])
- url – URL для отправки запроса.
- options – дополнительные параметры: метод, заголовки и так далее.
Без options это простой GET-запрос, скачивающий содержимое по адресу url. Браузер сразу же начинает запрос и возвращает promise, который внешний код использует для получения результата. Процесс получения ответа обычно происходит в два этапа.
Во-первых, promise
выполняется с объектом встроенного класса Response в качестве результата, как только сервер пришлёт заголовки ответа.
На этом этапе мы можем проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.
Promise завершается с ошибкой, если fetch
не смог выполнить HTTP-запрос, например при ошибке сети или если нет такого сайта. HTTP-статусы 404
и 500
не являются ошибкой.
Мы можем увидеть HTTP-статус в свойствах ответа:
status
– код статуса HTTP-запроса, например 200.
ok
– логическое значение: будет true
, если код HTTP-статуса в диапазоне 200-299.
1fetch(url).then( function(response){2 if (response.ok) { // если HTTP-статус в диапазоне 200-2993 // получаем тело ответа (см. про этот метод ниже)4 return response.json();5} else {6 alert("Ошибка HTTP: " + response.status);7}89})10.then(function(result) {11 console.log('result',result)12});
Для получения тела ответа нам нужно использовать дополнительный вызов метода.
Response
предоставляет несколько методов, основанных на промисах, для доступа к телу ответа в различных форматах:
response.text()
– читает ответ и возвращает как обычный текст,response.json()
– декодирует ответ в формате JSON,response.formData()
– возвращает ответ как объект FormData (разберём его в следующей главе),response.blob()
– возвращает объект как Blob (бинарные данные с типом),response.arrayBuffer()
– возвращает ответ как ArrayBuffer (низкоуровневое представление бинарных данных),
помимо этого, response.body
– это объект ReadableStream, с помощью которого можно считывать тело запроса по частям.
Например, получим JSON-объект со списком стран:
1const url = "https://restcountries.eu/rest/v2/all";2fetch(url).then(function(response) {3return response.json()4}).then(function(countries) {5 alert(countries[0].name);6});
Полный пример получения списка стран с помощью Fetch