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

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

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

      if (req.status < 400)

        succeed(req.responseText);

      else

        fail(new Error("Request failed: " + req.statusText));

    });

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

      fail(new Error("Network error"));

    });

    req.send(null);

  });

}

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

get("example/data.txt").then(function(text) {

  console.log("data.txt: " + text);

}, function(error) {

  console.log("Failed to fetch data.txt: " + error);

});

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

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

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

function getJSON(url) {

  return get(url).then(JSON.parse);

}

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

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

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

<script>

  function showMessage(msg) {

    var elt = document.createElement("div");

    elt.textContent = msg;

    return document.body.appendChild(elt);

  }

  var loading = showMessage("Загрузка...");

  getJSON("example/bert.json").then(function(bert) {

    return getJSON(bert.spouse);

  }).then(function(spouse) {

    return getJSON(spouse.mother);

  }).then(function(mother) {

    showMessage("Имя - " + mother.name);

  }).catch(function(error) {

    showMessage(String(error));

  }).then(function() {

    document.body.removeChild(loading);

  });

</script>

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

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

Цените HTTP

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

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