С момента публикации второй части перевода прошло довольно много времени. Так что Вам уже, наверняка, не терпится узнать, чем конкретно могут помочь промисы в повседневном JavaScript'е. Как раз об этом и пойдет речь в данном фрагменте перевода. Здесь начнется рассмотрение наиболее распространенной задачи из тех, которых помогают решить промисы. Это задача обработки набора связанных данных, получаемых с помощью нескольких запросов на сервер. В данном случаи, речь пойдет о выводе на экран текста, состоящего из разделов. При чем, необходимо выполнить один запрос, для получения списка URI всех разделов, и по одному запросу, для вычитки каждого параграфа. Хотя, как верно замечено, в исходном тексте постановка задачи несколько искусственна, она все же дает понять о том, как именно промисы позволяют решать подобные этой реальные задачи. Ну что ж, к делу.

Сложный асинхронный код теперь проще



Именно так, давайте напишем кое-что. Например, Вы хотите:

1. Запустить индикатор загрузки
2. Получить некоторый JSON истории, который содержит ее название, и ссылки на каждый из ее разделов
3.Отобразить название на странице
4. Получить по ссылке каждый раздел
5. Отобразить историю на странице
6. Остановить индикатор загрузки

…но к тому же, известить пользователя, если что-то пошло не так в процессе. Мы захотим так же остановить индикатор загрузки в этом случаи, иначе он будет крутится, у него закружится голова и он врежется в какой-то другой элемент UI.

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

Для начала, давайте разберемся с получением данных по сети.

Промисификация XMLHttpRequest



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

function get(url) {
// Вернуть новый промис.
return new Promise(function(resolve, reject) {
// Выполнить стандартные XHR процедуры
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
// Эта функция вызывается, даже в случаи кода 404 и подобных
// так что проверим статус
if (req.status == 200) {
// Вызовем функцию resolve с текстом ответа
resolve(req.response);
}
else {
// В противном случаи reject с текстом состояния
// который, будем надеяться, окажется какой-то понятной ошибкой
reject(Error(req.statusText));
}
};

// Обработаем ошибки сети
req.onerror = function() {
reject(Error("Ошибка сети"));
};

// Выполним запрос
req.send();
});
}


Теперь давайте используем ее:

get('story.json').then(function(response) {
console.log("Успех!", response);
}, function(error) {
console.error("Неудача!", error);
});


Вы можете проверить этот код в действии и посмотреть результат его выполнения в консоли DevTools. Теперь мы можем выполнять HTTP запросы, без набора XMLHttpRequest вручную, и это чудесно, поскольку, чем реже мне нужно будет видеть приводящий меня в ярость кемел-кейс XMLHttpRequest, тем счастливее будет моя жизнь.

Цепные вызовы



"then" — еще не конец истории, Вы можете выстраивать цепи из then'ов для преобразования значений или для последовательного вызова дополнительных асинхронных действий.

Преобразование значений



Вы можете приобразовывать значения просто возвращая новое значение:
var promise = new Promise(function(resolve, reject) {
resolve(1);
});

promise.then(function(val) {
console.log(val); // 1
return val + 2;
}).then(function(val) {
console.log(val); // 3
});


Как практический пример, давайте рассмотрим предыдущий случай:
get('story.json').then(function(response) {
console.log("Успех!", response);
});


В данной ситуации response это JSON, но мы получаем его в виде текста. Мы можем изменить нашу функцию get, и использовать тип ответа JSON, но мы можем решить эту задачу и в плоскости промисов:
get('story.json').then(function(response) {
return JSON.parse(response);
}).then(function(response) {
console.log("Ура JSON!", response);
});


Поскольку JSON.parse Принимает единственный аргумент и возвращает измененное значение(прим. пер.: а так же не привязан к контексту объекта JSON), мы можем воспользоваться такой короткой записью:
get('story.json').then(JSON.parse).then(function(response) {
console.log("Ура JSON!", response);
});


Вы можете проверить этот код в действии и посмотреть результат его выполнения в консоли DevTools. К тому же, мы можем очень просто написать функцию getJSON:
function getJSON(url) {
return get(url).then(JSON.parse);
}


getJSON по прежнему возвращает промис, который вначале получает данные по URL, а затем преобразует ответ в JSON.

Запросы на асинхронные действия



Вы так же можете создавать цепи then'ов для запуска последовательностей асинхронных вызовов.
Когда вы возвращаете что-либо из функции-параметра "then" — это немного магия. Если вы возвращаете значение, то с этим значением вызывается следующий "then". Если же вы возвращаете промисообразный объект, то следующий "then" ожидает его выполнения, и вызывается лишь тогда, когда этот промис переходит в состояние settled (завершается успешно или нет). Например:
getJSON('story.json').then(function(story) {
return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
console.log("Получен раздел 1!", chapter1);
});


Здесь мы делаем асинхронный запрос на "story.json", который возвращает набор URL для запроса разделов, затем мы запрашиваем первый из них. Это ситуация, когда промисы действительно начинаю становится чем-то большим, чем простым шаблоном из функций-колбеков. Вы можете даже сделать функцию для получения глав:
var storyPromise;

function getChapter(i) {
storyPromise = storyPromise || getJSON('story.json');

return storyPromise.then(function(story) {
return getJSON(story.chapterUrls[i]);
})
}

// и использовать ее очень просто:
getChapter(0).then(function(chapter) {
console.log(chapter);
return getChapter(1);
}).then(function(chapter) {
console.log(chapter);
});

Мы не скачиваем "story.json" до тех пор пока getChapter не будет вызван, но при последующих вызовах getChapter мы используем промис повторно, таким образом story.json запрашивается всего единожды. Ура промисам!
0

Комментарии

Для того, чтоб оставлять комментарии или зарегистрируйтесь.