что такое асинхронный запрос на сервер

Что такое AJAX? Создание асинхронных запросов

что такое асинхронный запрос на сервер. Смотреть фото что такое асинхронный запрос на сервер. Смотреть картинку что такое асинхронный запрос на сервер. Картинка про что такое асинхронный запрос на сервер. Фото что такое асинхронный запрос на сервер

Урок, в котором на примерах рассмотрим создание простых асинхронных AJAX запросов к серверу. В качестве метода передачи запросов будем использовать как метод GET, так и метод POST. На сервере обработку запросов выполним с помощью скриптов PHP.

Что такое AJAX?

AJAX – это аббревиатура от « A synchronous J avaScript a nd X ML», которая дословно переводится как «асинхронный JavaScript и XML».

Что означают эти слова?

Что такое AJAX? AJAX – это стек технологий, с помощью которых можно в фоновом режиме получать данные с сервера и использовать их для обновления страницы без её перезагрузки. Это делает сайты и веб-приложения более быстрыми и отзывчивыми при взаимодействии пользователей с ними.

Какие преимущества даёт технология AJAX при использовании её на сайте?

AJAX на сайте позволяет:

Создание асинхронных запросов с помощью XHR

Что такое асинхронный запрос, мы уже разобрали выше. Т.е. это такой, который выполняется в фоне и не мешает пользователю взаимодействовать со страницей.

А это значит, что при отправке такого запроса, страница не «замораживается», с ней можно работать дальше.

1. Начинается написания запроса с создания экземпляра объекта XMLHttpRequest :

2. После этого следует инициализировать запрос с помощью метода open() :

3. Следующий шаг – это назначить обработчик на событие readystatechange объекта xhr :

readyState – это свойство, содержащее числовой код, по которому можно определить в какой сейчас стадии находится запрос.

Статусы кодов readyState :

Из всех этих статусов нам интересен только 4. Он будет означать, что ответ от сервера получен и его можно обработать. Остальные коды на практике практически не используются.

Таким образом напишем проверку на равенство значения readyState числу 4:

Если запрос был успешно выполнен сервером, то его статус будет 200. Другие ответы нам в большинстве случаев не интересны. Например, если status равен 404 (запрашиваемый URL не найден), то в этом случае запрашиваемых данных нет и мы можем только как-то обработать это ошибку.

Добавим ещё одно условие в код: проверку status на равенство 200.

Теперь, если всё в порядке, мы можем получить данные и делать с ними всё, то угодно.

Получить данные (ответ от сервера) можно в виде строки ( xhr.responseText ) или объекта XML Document ( xhr.responseXML ).

Например, выведем полученный ответ от севера в консоль:

Если запрос асинхронный, то выполнение send() не останавливает дальнейшее выполнение программы. В противном случае (если запрос синхронный), программа приостанавливается и возобновляет своё выполнение только после получения ответа от сервера.

Пример AJAX GET запроса

В качестве примера напишем асинхронный AJAX запрос на чистом JavaScript, который будет после готовности DOM и при нажатии на кнопку обновлять содержимое списка на странице.

1. Для генерации некоторых данных на сервере создадим простой php-файл, который будет возвращать массив из 3 элементов в формате JSON.

В качестве сервера можно использовать «Open Server Panel», встроенный в PHP веб-сервер или любой другой.

2. Создадим HTML файл и поместим в следующее содержимое:

Пример AJAX POST запроса

что такое асинхронный запрос на сервер. Смотреть фото что такое асинхронный запрос на сервер. Смотреть картинку что такое асинхронный запрос на сервер. Картинка про что такое асинхронный запрос на сервер. Фото что такое асинхронный запрос на сервер

Создание асинхронного AJAX запроса (метод POST)

Изменим вышеприведённый пример. Теперь AJAX запрос к серверу будет выполняться после нажатию на кнопку. Он будет получать имя, которое ввёл пользователь в элемент input и отправлять его посредством метода POST на сервер. После получения ответа с сервера, заменим им содержимое элемента div на странице.

Источник

Синхронные и асинхронные запросы

XMLHttpRequest поддерживает как синхронные, так и асинхронные запросы. В основном предпочтительно использовать асинхронные запросы вместо синхронных из-за соображений производительности.

Синхронный запрос приводит к выполнению кода, который «блокирует» взаимодействие с вкладкой браузера до тех пор, пока запрос не будет полностью выполнен, что существенно ухудшает отклик страницы.

Асинхронные запросы

Пример: отправка запроса и получение файла ответа

2 строка. 3 параметр метода open установлен как true для того, чтобы явно указать, что этот запрос будет обрабатываться асинхронно.

15 строка. Происходит инициализация отправки запроса. Функция обработчик будет вызываться каждый раз, как будет происходить изменения состояния данного запроса.

Пример: создание стандартной функции для чтения внешних файлов

В разных сценариях существует необходимость принимать внешние файлы (ответ от сервера, к примеру, json файл). Это стандартная функция, которая использует
XMLHttpRequest объект асинхронно, чтобы передать прочитанный контент в специальную функцию обработчик.

Строка 5 определяет функцию, которая будет вызвана в случаи, если ajax запрос не сможет завершиться успешно.

Строка 11 сохраняет в XHR объекте функцию, которая будет вызвана после успешного завершения ajax запроса. (эта функция передаётся 2 аргументов в вызове функции loadFile ).

Строка 15 устанавливает true для 3 параметра, что явно указывает на то, что запрос будет выполняться асинхронно.

Строка 16 инициализирует запрос.

Пример: использование timeout

При использовании асинхронных запросов, можно установить максимальное время ожидания ответа от сервера. Это делается путём установки значения свойства timeout XMLHttpRequest объекта, как показано ниже:

2 аргумент функции loadFile устанавливает время ожидание равное 2000ms.

Synchronous request

Synchronous XHR often causes hangs on the web. But developers typically don’t notice the problem because the hang only manifests during poor network conditions or slow server response. Synchronous XHR is now in deprecation state. Developers are recommended to move away from the API.

Example: HTTP synchronous request

This example demonstrates how to make a simple synchronous request.

Line 3 sends the request. The null parameter indicates that no body content is needed for the GET request.

Example: Synchronous HTTP request from a Worker

example.html (the main page):

myFile.txt (the target of the synchronous XMLHttpRequest invocation):

myTask.js (the Worker ):

It could be useful in order to interact in the background with the server or to preload some content. See Using web workers for examples and details.

Adapting Sync XHR usecases to the Beacon API

There are some cases in which the synchronous usage of XMLHttpRequest was not replaceable, like during the window.onunload and window.onbeforeunload events. You should consider using the fetch API with keepalive flag. When fetch with keepalive isn’t available, you can consider using the navigator.sendBeacon API can support these use cases typically while delivering a good UX.

The following example (from the sendBeacon docs) shows a theoretical analytics code that attempts to submit data to a server by using a synchronous XMLHttpRequest in an unload handler. This results in the unloading of the page to be delayed.

Using the sendBeacon() method, the data will be transmitted asynchronously to the web server when the User Agent has had an opportunity to do so, without delaying the unload or affecting the performance of the next navigation.

The following example shows a theoretical analytics code pattern that submits data to a server by using the sendBeacon() method.

Источник

Основы XMLHttpRequest

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/xmlhttprequest.

Объект XMLHttpRequest (или, как его кратко называют, «XHR») даёт возможность из JavaScript делать HTTP-запросы к серверу без перезагрузки страницы.

Несмотря на слово «XML» в названии, XMLHttpRequest может работать с любыми данными, а не только с XML.

Использовать его очень просто.

Пример использования

Как правило, XMLHttpRequest используют для загрузки данных.

Для начала посмотрим на пример использования, который загружает файл phones.json из текущей директории и выдаёт его содержимое:

Настроить: open

Задаёт основные параметры запроса:

method – HTTP-метод. Как правило, используется GET либо POST, хотя доступны и более экзотические, вроде TRACE/DELETE/PUT и т.п.

«Синхронный запрос» означает, что после вызова xhr.send() и до ответа сервера главный поток будет «заморожен»: посетитель не сможет взаимодействовать со страницей – прокручивать, нажимать на кнопки и т.п. После получения ответа выполнение продолжится со следующей строки.

«Асинхронный запрос» означает, что браузер отправит запрос, а далее результат нужно будет получить через обработчики событий, которые мы рассмотрим далее.

Отослать данные: send

Именно этот метод открывает соединение и отправляет запрос на сервер.

Отмена: abort

Вызов xhr.abort() прерывает выполнение запроса.

Ответ: status, statusText, responseText

Основные свойства, содержащие ответ сервера:

Есть и ещё одно свойство, которое используется гораздо реже:

Синхронные и асинхронные запросы

Если синхронный вызов занял слишком много времени, то браузер предложит закрыть «зависшую» страницу.

Из-за такой блокировки получается, что нельзя отослать два запроса одновременно. Кроме того, забегая вперёд, заметим, что ряд продвинутых возможностей, таких как возможность делать запросы на другой домен и указывать таймаут, в синхронном режиме не работают.

Из всего вышесказанного уже должно быть понятно, что синхронные запросы используются чрезвычайно редко, а асинхронные – почти всегда.

Полный пример в действии:

Событие readystatechange

В примере выше мы использовали только состояние 4 (запрос завершён), но есть и другие.

При состоянии readyState=3 (получен очередной пакет) мы можем посмотреть текущие данные в responseText и, казалось бы, могли бы работать с этими данными как с «ответом на текущий момент».

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

Чем это опасно? Хотя бы тем, что символы русского языка в кодировке UTF-8 кодируются двумя байтами каждый – и разрыв может возникнуть между ними.

Получится, что при очередном readyState в конце responseText будет байт-полсимвола, то есть он не будет корректной строкой – частью ответа! Если в скрипте как-то по-особому это не обработать, то неизбежны проблемы.

HTTP-заголовки

XMLHttpRequest умеет как указывать свои заголовки в запросе, так и читать присланные в ответ.

Для работы с HTTP-заголовками есть 3 метода:

Нельзя установить заголовки, которые контролирует браузер, например Referer или Host и ряд других (полный список тут).

Это ограничение существует в целях безопасности и для контроля корректности запроса.

Особенностью XMLHttpRequest является то, что отменить setRequestHeader невозможно.

Повторные вызовы лишь добавляют информацию к заголовку, например:

Заголовки возвращаются в виде единой строки, например:

Таким образом, если хочется получить объект с парами заголовок-значение, то эту строку необходимо разбить и обработать.

Таймаут

Максимальную продолжительность асинхронного запроса можно задать свойством timeout :

При превышении этого времени запрос будет оборван и сгенерировано событие ontimeout :

Полный список событий

Современная спецификация предусматривает следующие события по ходу обработки запроса:

Используя эти события можно более удобно отслеживать загрузку ( onload ) и ошибку ( onerror ), а также количество загруженных данных ( onprogress ).

В современных браузерах от него можно отказаться в пользу других, необходимо лишь, как мы увидим далее, учесть особенности IE8-9.

IE8,9: XDomainRequest

В IE8 и IE9 поддержка XMLHttpRequest ограничена:

А обычный XMLHttpRequest решили не трогать, чтобы ненароком не сломать существующий код.

IE9- и кеширование

Обычно ответы на запросы XMLHttpRequest кешируются, как и обычные страницы.

Альтернативный вариант – добавить в URL запроса случайный параметр, предотвращающий кеширование.

Например, вместо xhr.open(‘GET’, ‘service’, false) написать:

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

Итого

Типовой код для GET-запроса при помощи XMLHttpRequest :

Мы разобрали следующие методы XMLHttpRequest :

Задачи

Выведите телефоны

Создайте код, который загрузит файл phones.json из текущей директории и выведет все названия телефонов из него в виде списка.

Исходный код просто выводит содержимое файла (скачайте к себе):

Источник

Что такое AJAX

Как добавить что-то новое на страницу без перезагрузки

Когда мы делали форму обратной связи на сайте, то она работала так:

Часто это неудобно: хочется оставить посетителя на странице, а не показывать что-то новое. Чтобы сделать именно так, используют AJAX.

AJAX (программисты называют его «аякс») — это сокращение от Asynchronous Javascript and XML, а на русский это переводится как «Асинхронный JavaScript и XML». Смысл здесь — в асинхронности действий браузера и сервера. Разберём эти понятия.

Синхронные запросы

Обычные запросы к серверу, чтобы получить и сформировать страницу, работают так:

Синхронность здесь в том, что все данные кучей отправляются на сервер, а потом вся страница такой же охапкой возвращается пользователю.

что такое асинхронный запрос на сервер. Смотреть фото что такое асинхронный запрос на сервер. Смотреть картинку что такое асинхронный запрос на сервер. Картинка про что такое асинхронный запрос на сервер. Фото что такое асинхронный запрос на сервер

Именно так работает наша форма обратной связи: вы нажимаете на кнопку и браузер, чтобы показать новые данные, начинает загружать новую страницу. Но есть и другой способ.

Асинхронные запросы

AJAX работает иначе: вместо того чтобы синхронно загружать новые данные с новой страницей, он запрашивает у сервера недостающую информацию и добавляет её на страницу. А сама страница не перезагружается.

В нашем примере с формой AJAX будет работать так:

что такое асинхронный запрос на сервер. Смотреть фото что такое асинхронный запрос на сервер. Смотреть картинку что такое асинхронный запрос на сервер. Картинка про что такое асинхронный запрос на сервер. Фото что такое асинхронный запрос на сервер

Примеры таких запросов:

Как устроен AJAX

Основа AJAX — объект XMLHttpRequest, который и отвечает за все запросы. Раньше его поддерживали не все браузеры, поэтому нужно было подключить отдельно библиотеку jQuery, в которой этот объект есть. Сейчас почти все браузеры научились поддерживать этот объект напрямую.

Вся магия асинхронных запросов AJAX основана на работе с DOM-объектами страницы в браузере:

Плюсы и минусы AJAX

✅ Удобство для посетителя и быстрый интерфейс. Гораздо лучше видеть моментальное обновление данных на той же странице, чем загружать новую при каждой новой порции данных.

✅ Меньше трафика. Если нужно показать что-то новое, браузер получает с сервера не всю страницу целиком, а только то, чего нет на исходной странице.

✅ Можно снизить нагрузку на сервер. Если сайт формирует все страницы на лету, например, с помощью PHP, то можно один раз загрузить стандартные части страницы (шапку, меню и подвал), а остальное подгружать по необходимости. Меньше данных в запросе — быстрее ответ от сервера и базы.

Нужен включённый JavaScript. Если в браузере выключить JS, то ничего не сработает — данные не придут с сервера и интерактивной страницы не получится.

Поисковые движки не видят AJAX-контент. Если содержимое страницы формируется на AJAX-запросах, то поисковые роботы не смогут его увидеть. Смысл в том, что поисковики смотрят на исходный код, а не на то, что приходит с сервера. Поисковая оптимизация таких страниц — головная боль для сеошника.

Растёт сложность проекта. Работа с такими запросами требует определённой квалификации от программиста, чтобы он мог предусмотреть разные нештатные ситуации и обработать их заранее. А ещё нужно продумать бэкенд — поведение сервера в ответ на разные запросы.

Ошибки при нестабильной связи. Если интернет работает с перебоями, то AJAX может не дождаться ответа от сервера или не сможет отправить свой запрос. В итоге логика работы страницы может нарушиться — в итоге придётся перезагружать её полностью и начинать работу с нуля.

Что дальше

Чтобы посмотреть, как AJAX работает и показывает новые данные без перезагрузки, сделаем пару небольших проектов. Заодно попрактикуемся в PHP — он будет отвечать за новые данные с сервера.

Источник

Выразительный JavaScript: HTTP

Содержание

Мечта, ради которой создавалась Сеть – это общее информационное пространство, в котором мы общаемся, делясь информацией. Его универсальность является его неотъемлемой частью: ссылка в гипертексте может вести куда угодно, будь то персональная, локальная или глобальная информация, черновик или выверенный текст.

Тим Бернес-Ли, Всемирная паутина: Очень короткая личная история

Протокол

Если в адресной строке браузера набрать eloquentjavascript.net/17_http.html, браузер сначала распознает адрес сервера, связанный с именем eloquentjavascript.net и попробует открыть TCP соединение по 80 порту – порт для HTTP по умолчанию. Если сервер существует и принимает соединение, браузер отправляет что-то вроде:

GET /17_http.html HTTP/1.1
Host: eloquentjavascript.net
User-Agent: Название браузера

Сервер отвечает по тому же соединению:

HTTP/1.1 200 OK
Content-Length: 65585
Content-Type: text/html
Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT

Браузер берёт ту часть, что идёт за ответом после пустой строки и показывает её в виде HTML-документа.

Информация, отправленная клиентом, называется запросом. Он начинается со строки:

GET /17_http.html HTTP/1.1

Первое слово – метод запроса. GET означает, что нам нужно получить определённый ресурс. Другие распространённые методы – DELETE для удаления, PUT для замещения и POST для отправки информации. Заметьте, что сервер не обязан выполнять каждый полученный запрос. Если вы выберете случайный сайт и скажете ему DELETE главную страницу – он, скорее всего, откажется.

Часть после названия метода – путь к ресурсу, к которому отправлен запрос. В простейшем случае, ресурс – просто файл на сервере, но протокол не ограничивается этой возможностью. Ресурс может быть чем угодно, что можно передать в качестве файла. Многие серверы создают ответы на лету. К примеру, если вы откроете twitter.com/marijnjh, сервер посмотрит в базе данных пользователя marijnjh, и если найдёт – создаст страницу профиля этого пользователя.

После пути к ресурсу первая строка запроса упоминает HTTP/1.1, чтобы сообщить о версии HTTP – протокола, которую она использует.

Ответ сервера также начинается с версии протокола, за которой идёт статус ответа – сначала код из трёх цифр, затем строчка.

Коды статуса, начинающиеся с 2, обозначают успешные запросы. Коды, начинающиеся с 4, означают, что что-то пошло не так. 404 – самый знаменитый статус HTTP, обозначающий, что запрошенный ресурс не найден. Коды, начинающиеся с 5, обозначают, что на сервере произошла ошибка, но не по вине запроса.

За первой строкой запроса или ответа может идти любое число строк заголовка. Это строки в виде “имя: значение”, которые обозначают дополнительную информацию о запросе или ответе. Эти заголовки были включены в пример:

Content-Length: 65585
Content-Type: text/html
Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT

Тут определяется размер и тип документа, полученного в ответ. В данном случае это HTML-документ размером 65’585 байт. Также тут указано, когда документ был изменён последний раз.

По большей части клиент или сервер определяют, какие заголовки необходимо включать в запрос или ответ, хотя некоторые заголовки обязательны. К примеру, Host, обозначающий имя хоста, должен быть включён в запрос, потому что один сервер может обслуживать много имён хостов на одном ip-адресе, и без этого заголовка сервер не узнает, с каким хостом клиент пытается общаться.

После заголовков, как запрос, так и ответ могут указать пустую строку, за которой следует тело, содержащее передаваемые данные. Запросы GET и DELETE не пересылают дополнительных данных, а PUT и POST пересылают. Некоторые ответы, например, сообщения об ошибке, не требуют наличия тела.

Браузер и HTTP

Как мы видели в примере, браузер отправляет запрос, когда мы вводим URL в адресную строку. Когда в полученном HTML документе содержатся упоминания других файлов, такие, как картинки или файлы JavaScript, они тоже запрашиваются с сервера.

Веб-сайт средней руки легко может содержать от 10 до 200 ресурсов. Чтобы иметь возможность запросить их побыстрее, браузеры делают несколько запросов одновременно, а не ждут окончания запросов одного за другим. Такие документы всегда запрашиваются через запросы GET.

На страницах HTML могут быть формы, которые позволяют пользователям вписывать информацию и отправлять её на сервер. Вот пример формы:

Код описывает форму с двумя полями: маленькое запрашивает имя, а большое – сообщение. При нажатии кнопки «Отправить» информация из этих полей будет закодирована в строку запроса (query string). Когда атрибут method элемента равен GET, или когда он вообще не указан, строка запроса помещается в URL из поля action, и браузер делает GET-запрос с этим URL.

GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1

Начало строки запроса обозначено знаком вопроса. После этого идут пары имён и значений, соответствующие атрибуту name полей формы и содержимому этих полей. Амперсанд (&) используется для их разделения.

Сообщение, отправляемое в примере, содержит строку “Yes?”, хотя знак вопроса и заменён каким-то странным кодом. Некоторые символы в строке запроса нужно экранировать (escape). Знак вопроса в том числе, и он представляется кодом %3F. Есть какое-то неписаное правило, по которому у каждого формата должен быть способ экранировать символы. Это правило под названием кодирование URL использует процент, за которым идут две шестнадцатеричные цифры, которые представляют код символа. В этом случае число 3F, или 63 в десятичной системе счисления — это код знака вопроса. У JavaScript есть функции encodeURIComponent и decodeURIComponent для кодирования и раскодирования.

Если мы поменяем атрибут method в форме в предыдущем примере на POST, запрос HTTP с отправкой формы пройдёт при помощи метода POST, который отправит строку запроса в теле запроса, вместо добавления её к URL.

POST /example/message.html HTTP/1.1
Content-length: 24
Content-type: application/x-www-form-urlencoded

По соглашению метод GET используется для запросов, не имеющих побочных эффектов, таких, как поиск. Запросы, которые что-то меняют на сервере – создают новый аккаунт или размещают сообщение, должны отправляться методом POST. Клиентские программы типа браузера знают, что просто так делать запросы формата POST не нужно, и иногда незаметно для пользователя делают запросы GET – к примеру, чтобы загрузить заранее контент, который может вскоре понадобиться пользователю.

В следующей главе мы вернёмся к формам и поговорим про то, как мы можем делать их при помощи JavaScript.

XMLHttpRequest

Интерфейс, через который JavaScript в браузере может делать HTTP-запросы, называется XMLHttpRequest (заметьте, как прыгает размер букв). Он был разработан в Microsoft для браузера Internet Explorer в конце 1990-х. В это время формат XML был очень популярным в мире бизнес-программ – а в этом мире Microsoft всегда чувствовал себя, как дома. Он был настолько популярным, что аббревиатура XML была пришпилена перед названием интерфейса для работы с HTTP, хотя последний с XML вообще не связан.

И всё же имя не полностью бессмысленное. Интерфейс позволяет разбирать вам ответы, как если бы это были документы XML. Смешивать две разные вещи (запрос и разбор ответа) в одну – это, конечно, отвратительный дизайн, но что поделаешь.

Когда интерфейс XMLHttpRequest был добавлен в Internet Explorer, стало можно делать вещи, которые раньше было делать очень сложно. К примеру, сайты стали показывать списки из подсказок, пока пользователь вводит что-либо в текстовое поле. Скрипт отправляет текст на сервер через HTTP одновременно с набором текста пользователем. Сервер, у которого есть база данных для возможных вариантов ввода, ищет среди записей подходящие и возвращает их назад для показа. Это выглядело очень круто – люди до этого привыкли ждать перезагрузки всей страницы после каждого взаимодействия с сайтом.

Другой важный браузер того времени Mozilla (позже Firefox), не хотел отставать. Чтобы разрешить делать сходные вещи, Mozilla скопировал интерфейс вместе с названием. Следующее поколение браузеров последовало этому примеру, и сегодня XMLHttpRequest является стандартом de facto.

Отправка запроса

Чтобы отправить простой запрос, мы создаём объект запроса с конструктором XMLHttpRequest и вызываем методы open и send.

Метод open настраивает запрос. В нашем случае мы решили сделать GET запрос на файл example/data.txt. URL, не начинающиеся с названия протокола (например, http:) называются относительными, то есть они интерпретируются относительно текущего документа. Когда они начинаются со слеша (/), они заменяют текущий путь – часть после названия сервера. В ином случае часть текущего пути вплоть до последнего слеша помещается перед относительным URL.

После открытия запроса мы можем отправить его методом send. Аргументом служит тело запроса. Для запросов GET используется null. Если третий аргумент для open был false, то send вернётся только после того, как был получен ответ на наш запрос. Для получения тела ответа мы можем прочесть свойство responseText объекта request.

Можно получить из объекта response и другую информацию. Код статуса доступен в свойстве status, а текст статуса – в statusText. Заголовки можно прочесть из getResponseHeader.

Названия заголовков не чувствительны к регистру. Они обычно пишутся с заглавной буквы в начале каждого слова, например “Content-Type”, но “content-type” или “cOnTeNt-TyPe” будут описывать один и тот же заголовок.

Браузер сам добавит некоторые заголовки, такие, как “Host” и другие, которые нужны серверу, чтобы вычислить размер тела. Но вы можете добавлять свои собственные заголовки методом setRequestHeader. Это нужно для особых случаев и требует содействия сервера, к которому вы обращаетесь – он волен игнорировать заголовки, которые он не умеет обрабатывать.

Асинхронные запросы

В примере запрос был окончен, когда заканчивается вызов send. Это удобно потому, что свойства вроде responseText становятся доступными сразу. Но это значит, что программа наша будет ожидать, пока браузер и сервер общаются меж собой. При плохой связи, слабом сервере или большом файле это может занять длительное время. Это плохо ещё и потому, что никакие обработчики событий не сработают, пока программа находится в режиме ожидания – документ перестанет реагировать на действия пользователя.

Если третьим аргументом open мы передадим true, запрос будет асинхронным. Это значит, что при вызове send запрос ставится в очередь на отправку. Программа продолжает работать, а браузер позаботиться об отправке и получении данных в фоне.

Но пока запрос обрабатывается, мы не получим ответ. Нам нужен механизм оповещения о том, что данные поступили и готовы. Для этого нам нужно будет слушать событие “load”.

Так же, как вызов requestAnimationFrame в главе 15, этот код вынуждает нас использовать асинхронный стиль программирования, оборачивая в функцию тот код, который должен быть выполнен после запроса, и устраивая вызов этой функции в нужное время. Мы вернёмся к этому позже.

Получение данных XML

Когда ресурс, возвращённый объектом XMLHttpRequest, является документом XML, свойство responseXML будет содержать разобранное представление о документе. Оно работает схожим с DOM образом, за исключением того, что у него нет присущей HTML функциональности навроде свойства style. Объект, содержащийся в responseXML, соответствует объекту document. Его свойство documentElement ссылается на внешний тег документа XML. В следующем документе (example/fruit.xml) таким тегом будет :

Мы можем получить такой файл следующим образом:

Документы XML можно использовать для обмена с сервером структурированной информацией. Их форма – вложенные теги – хорошо подходит для хранения большинства данных, ну или по крайней мере лучше, чем текстовые файлы. Интерфейс DOM неуклюж в плане извлечения информации, и XML документы получаются довольно многословными. Обычно лучше общаться при помощи данных в формате JSON, которые проще читать и писать – как программам, так и людям.

Песочница для HTTP

HTTP-запросы из веб-страницы вызывают вопросы касаемо безопасности. Человек, контролирующий скрипт, может иметь интересы отличные от интересов пользователя, на чьём компьютере он запущен. Конкретно, если я зашёл на сайт themafia.org, я не хочу, чтобы их скрипты могли делать запросы к mybank.com, используя информацию моего браузера в качестве идентификатора, и давая команду отправить все мои деньги на какой-нибудь счёт мафии.

Вебсайты могут защитить себя от подобных атак, но для этого требуются определённые усилия, и многие сайты с этим не справляются. Из-за этого браузеры защищают их, запрещая скриптам делать запросы к другим доменам (именам вроде themafia.org и mybank.com).

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

Абстрагируем запросы

В главе 10 в нашей реализации модульной системы AMD мы использовали гипотетическую функцию backgroundReadFile. Она принимала имя файла и функцию, и вызывала эту функцию после прочтения содержимого файла. Вот простая реализация этой функции:

Простая абстракция упрощает использование XMLHttpRequest для простых GET-запросов. Если вы пишете программу, которая делает HTTP-запросы, будет неплохо использовать вспомогательную функцию, чтобы вам не приходилось всё время повторять уродливый шаблон XMLHttpRequest.

Аргумент callback (обратный вызов) – термин, часто использующийся для описания подобных функций. Функция обратного вызова передаётся в другой код, чтобы он мог позвать нас обратно позже.

Несложно написать свою вспомогательную функцию HTTP, специально скроенную под вашу программу. Предыдущая делает только GET-запросы, и не даёт нам контроля над заголовками или телом запроса. Можно написать ещё один вариант для запроса POST, или более общий, поддерживающий разные запросы. Многие библиотеки JavaScript предлагают обёртки для XMLHttpRequest.

Основная проблема с приведённой обёрткой – обработка ошибок. Когда запрос возвращает код статуса, обозначающий ошибку (от 400 и выше), он ничего не делает. В некоторых случаях это нормально, но представьте, что мы поставили индикатор загрузки на странице, показывающий, что мы получаем информацию. Если запрос не удался, потому что сервер упал или соединение прервано, страница будет делать вид, что она чем-то занята. Пользователь подождёт немного, потом ему надоест и он решит, что сайт какой-то дурацкий.

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

Обработка ошибок в асинхронном коде ещё сложнее, чем в синхронном. Поскольку нам часто приходится отделять часть работы и размещать её в функции обратного вызова, область видимости блока try теряет смысл. В следующем коде исключение не будет поймано, потому что вызов backgroundReadFile возвращается сразу же. Затем управление уходит из блока try, и функция из него не будет вызвана.

Чтобы обрабатывать неудачные запросы, придётся передавать дополнительную функцию в нашу обёртку, и вызывать её в случае проблем. Другой вариант – использовать соглашение, что если запрос не удался, то в функцию обратного вызова передаётся дополнительный аргумент с описанием проблемы. Пример:

Мы добавили обработчик события error, который сработает при проблеме с вызовом. Также мы вызываем функцию обратного вызова с аргументом error, когда запрос завершается со статусом, говорящим об ошибке.

Код, использующий getURL, должен проверять не возвращена ли ошибка, и обрабатывать её, если она есть.

С исключениями это не помогает. Когда мы совершаем последовательно несколько асинхронных действий, исключение в любой точке цепочки в любом случае (если только вы не обернули каждый обработчик в свой блок try/catch) вывалится на верхнем уровне и прервёт всю цепочку.

Обещания

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

Предпринималось множество попыток решить эту проблему дополнительными абстракциями. Одна из наиболее удачных попыток называется обещаниями (promises). Обещания оборачивают асинхронное действие в объект, который может передаваться и которому нужно сделать какие-то вещи, когда действие завершается или не удаётся. Такой интерфейс уже стал частью текущей версии JavaScript, а для старых версий его можно использовать в виде библиотеки.

Интерфейс обещаний не особенно интуитивно понятный, но мощный. В этой главе мы лишь частично опишем его. Больше информации можно найти на www.promisejs.org.

Для создания объекта promises мы вызываем конструктор Promise, задавая ему функцию инициализации асинхронного действия. Конструктор вызывает эту функцию и передаёт ей два аргумента, которые сами также являются функциями. Первая должна вызываться в удачном случае, другая – в неудачном.

И вот наша обёртка для запросов GET, которая на этот раз возвращает обещание. Теперь мы просто назовём его get.

Заметьте, что интерфейс к самой функции упростился. Мы передаём ей URL, а она возвращает обещание. Оно работает как обработчик для выходных данных запроса. У него есть метод then, который вызывается с двумя функциями: одной для обработки успеха, другой – для неудачи.

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

Вызов then производит новое обещание, чей результат (значение, передающееся в обработчики успешных результатов) зависит от значения, возвращаемого первой переданной нами в then функцией. Эта функция может вернуть ещё одно обещание, обозначая что проводится дополнительная асинхронная работа. В этом случае обещание, возвращаемое then само по себе будет ждать обещания, возвращённого функцией-обработчиком, и успех или неудача произойдут с таким же значением. Когда функция-обработчик возвращает значение, не являющееся обещанием, обещание, возвращаемое then, становится успешным, в качестве результата используя это значение.

Значит, вы можете использовать then для изменения результата обещания. К примеру, следующая функция возвращает обещание, чей результат – содержимое с данного URL, разобранное как JSON:

Последний вызов then не обозначил обработчик неудач. Это допустимо. Ошибка будет передана в обещание, возвращаемое через then, а ведь это нам и надо – getJSON не знает, что делать, когда что-то идёт не так, но есть надежда, что вызывающий её код это знает.

В качестве примера, показывающего использование обещаний, мы напишем программу, получающую число JSON-файлов с сервера, и показывающую во время исполнения запроса слово «загрузка». Файлы содержат информацию о людях и ссылки на другие файлы с информацией о других людях в свойствах типа отец, мать, супруг.

Нам нужно получить имя матери супруга из example/bert.json. В случае проблем нам нужно убрать текст «загрузка» и показать сообщение об ошибке. Вот как это можно делать при помощи обещаний:

Итоговая программа относительно компактна и читаема. Метод catch схож с then, но он ожидает только обработчик неудачного результата и в случае успеха передаёт дальше неизменённый результат. Исполнение программы будет продолжено обычным путём после отлова исключения – так же, как в случае с try/catch. Таким образом, последний then, удаляющий сообщение о загрузке, выполняется в любом случае, даже в случае неудачи.

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

Цените HTTP

При создании системы, в которой программа на JavaScript в браузере (клиентская) общается с серверной программой, можно использовать несколько вариантов моделирования такого общения.

Общепринятый метод – удалённые вызовы процедур. В этой модели общение идёт по шаблону обычных вызовов функций, только функции эти выполняются на другом компьютере. Вызов заключается в создании запроса на сервер, в который входят имя функции и аргументы. Ответ на запрос включает возвращаемое значение.

При использовании удалённых вызовов процедур HTTP служит лишь транспортом для общения, и вы, скорее всего, напишете слой абстракции, который спрячет его полностью.

Другой подход – построить свою систему общения на концепции ресурсов и методов HTTP. Вместо удалённого вызова процедуры по имени addUser вы делаете запрос PUT к /users/larry. Вместо кодирования свойств пользователя в аргументах функции вы определяете формат документа или используете существующий формат, который будет представлять пользователя. Тело PUT-запроса, создающего новый ресурс, будет просто документом этого формата. Ресурс получается через запрос GET к его URL (/user/larry), который возвращает представляющий этот ресурс документ.

Второй подход упрощает использование некоторых возможностей HTTP, например поддержки кеширования ресурсов (копия ресурса хранится на стороне клиента). Также он способствует созданию согласованного интерфейса, потому что думать в терминах ресурсов проще, чем в терминах функций.

Безопасность и HTTPS

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

Если нужно хранить что-либо в секрете, например пароли к емейлу, или данным необходимо прийти в пункт назначения в неизменном виде — таким, например, как номер банковского счёта, на который вы переводите деньги,- простого HTTP недостаточно.

Безопасный протокол HTTP, URL которого начинаются с https://, оборачивает HTTP-трафик так, чтобы его было сложнее прочитать и поменять. Сначала клиент подтверждает, что сервер – тот, за кого себя выдаёт, требуя с сервера представить криптографический сертификат, выданный авторитетной стороной, которую признаёт браузер. Потом, все данные, проходящие через соединение, шифруются так, чтобы предотвратить прослушку и изменение.

Таким образом, когда всё работает правильно, HTTPS предотвращает как случаи, когда кто-то притворяется другим веб-сайтом, с которым вы общаетесь, так и случаи прослушки вашего общения. Он не идеален, и уже были случаи, когда HTTPS не справлялся с работой из-за поддельных или краденых сертификатов или сломанных программ. Тем не менее, с HTTP очень легко сделать что-то плохое, а взлом HTTPS требует таких усилий, которые могут прикладывать только государственные структуры или очень серьёзные криминальные организации (а между этими организациями иногда совсем нет различий).

В этой главе мы увидели, что HTTP – это протокол доступа к ресурсам в интернете. Клиент отправляет запрос, содержащий метод (обычно GET), и путь, который определяет ресурс. Сервер решает, что ему делать с запросом и отвечает с кодом статуса и телом ответа. Запросы и ответы могут содержать заголовки, в которых передаётся дополнительная информация.

Браузеры делают GET-запросы для получения ресурсов, необходимых для показа страницы. Страница может содержать формы, которые позволяют информации, введённой пользователем, быть отправленной в запросе, который создаётся после отправки формы. Вы узнаете об этом больше в следующей главе.

Интерфейс, через который JavaScript делает HTTP-запросы из браузера, называется XMLHttpRequest. Можно игнорировать приставку “XML” (но писать её всё равно нужно). Использовать его можно двумя способами: синхронным, который блокирует всю работу до окончания выполнения запроса, и асинхронным, который требует установки обработчика событий, отслеживающего окончание запроса. Почти во всех случаях предпочтительным является асинхронный способ. Создание запроса выглядит так:

Асинхронное программирование – непростая вещь. Обещания – интерфейс, который делает её проще, помогая направлять сообщения об ошибках и исключения к нужному обработчику, и абстрагируя некоторые повторяющиеся элементы, подверженные ошибкам.

Упражнения

Согласование содержания (content negotiation)

Одна из вещей, которые HTTP умеет делать, но которую мы не обсуждали, называется согласованием содержания. Заголовок Accept в запросе можно использовать для сообщения серверу того, какие типы документов клиент желает получить. Многие серверы его игнорируют, но когда сервер знает о разных способах кодирования ресурса, он может взглянуть на заголовок и отправить тот, который предпочитает клиент.

URL eloquentjavascript.net/author настроен на ответ как прямым текстом, так и HTML или JSON, в зависимости от запроса клиента. Эти форматы определяются стандартизированными типами содержимого text/plain, text/html, и application/json.

Отправьте запрос для получения всех трёх форматов этого ресурса. Используйте метод setRequestHeader объекта XMLHttpRequest для установки заголовка Accept в один из нужных типов содержимого. Убедитесь, что вы устанавливаете заголовок после open, но перед send.

Наконец, попробуйте запросить содержимое типа application/rainbows+unicorns и посмотрите, что произойдёт.

Ожидание нескольких обещаний

У конструктора Promise есть метод all, который, получая массив обещаний, возвращает обещание, которое ждёт завершения всех указанных в массиве обещаний. Затем он выдаёт успешный результат и возвращает массив с результатами. Если какие-то из обещаний в массиве завершились неудачно, общее обещание также возвращает неудачу (со значением неудавшегося обещания из массива).

Попробуйте сделать что-либо подобное, написав функцию all.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *