Выбрать главу

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

var req = new XMLHttpRequest();

req.open("GET", "example/data.txt", true);

req.addEventListener("load", function() {

  console.log("Done:", req.status);

});

req.send(null);

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

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

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

<fruits>

  <fruit name="banana" color="yellow"/>

  <fruit name="lemon" color="yellow"/>

  <fruit name="cherry" color="red"/>

</fruits>

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

var req = new XMLHttpRequest();

req.open("GET", "example/fruit.xml", false);

req.send(null);

console.log(req.responseXML.querySelectorAll("fruit").length);

// → 3

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

var req = new XMLHttpRequest();

req.open("GET", "example/fruit.json", false);

req.send(null);

console.log(JSON.parse(req.responseText));

// → {banana: "yellow", lemon: "yellow", cherry: "red"}

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

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

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

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

Access-Control-Allow-Origin: *

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

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

function backgroundReadFile(url, callback) {

  var req = new XMLHttpRequest();

  req.open("GET", url, true);

  req.addEventListener("load", function() {

    if (req.status < 400)

      callback(req.responseText);

  });

  req.send(null);

}

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

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

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