Promises 101

Переклад першої частини відмінною статті про промисы. Базові прийоми створення і управління промисами.
Промисы використовуються для операцій, обчислення яких займає невизначений час. Прикладом такої операції може бути мережевий запит, коли ми запитуємо дані API і не можемо точно визначити, коли буде отримана відповідь.

Якщо є інші операції, виконання яких залежить від цього мережного запиту, то вимальовується проблема. Без промисов нам доведеться використовувати низку колбэков(callbacks), щоб вибудувати послідовність операцій. Це нормально, якщо у нас одне асинхронне дію. Але якщо потрібно зробити кілька послідовних асинхронних кроків, колбэки стають некерованими і результат сумно відомий як локшина колбеков (callback hell)
doSomething(function(responseOne) { 
doSomethingElse(responseOne, function(responseTwo, err) {
if (err) { handleError(err); }
doMoreStuff(responseTwo, function(responseThree, err) {
if (err) { handleAnotherError(err); }
doFinalThing(responseThree, function(err) {
if (err) { handleAnotherError(err); }
// Виконано
}); // кінець doFinalThing
}); // кінець doMoreStuff
}); // кінець doSomethingElse
}); // кінець doSomething

Промисы надають стандартизований і зрозумілий метод рішення завдань, які повинні виконуватися послідовно.
doSomething() 
.then(doSomethingElse)
.catch(handleError)
.then(doMoreStuff)
.then(doFinalThing)
.catch(handleAnotherError)

Створення промисов
Промисы створюються за допомогою конструктора промисов. Він являє собою функцію з двома аргументами (
resolve
&
reject
) в якості параметрів.
var promise = new Promise(function(resolve, reject) { /* Вміст промиса */ } ) 

Конструктор Промисов
Усередині цієї функції ми можемо виконувати будь-які асинхронні завдання. Щоб відзначити промис як виконаний, ми викликаємо
resolve()
, передаючи йому значення, яке ми хочемо повернути. Що б зазначити промис як відхилений або невдалий, ми викликаємо
reject()
, передаючи йому повідомлення помилки. До того, як промис стане сповненим або відхиленим, він знаходиться в стані очікування.
Ось промис версія XMLHttpRequest -
/* CREDIT - Jake Archibald, http://www.html5rocks.com/en/tutorials/es6/promises/ */

function get(url) { 
return new Promise(function(resolve, reject) {

var req = new XMLHttpRequest();
req.open('GET', url);

req.onload = function() {
if (req.status == 200) { 
resolve(req.response); /* ПРОМИС ВИКОНАНИЙ */
} else { 
reject(Error(req.statusText)); /* ПРОМИС ВІДХИЛЕНИЙ */
}
};

req.onerror = function() { reject(Error("Network Error")); };
req.send();
});
}

Використання промисов
Що б виконати промис, ми можемо викликати його як будь-яку звичайну функцію. Але, так як це промис, у нас є доступ до
.then
методом, який ми можемо додати до функції і який буде виконаний коли промис вийде з режиму очікування.
.then()
метод приймає два необов'язкові параметри. Перший — це функція, яка викликається, коли промис виконаний(resolved). Другий — функція, яка виконується якщо промис відхилено(rejected).
get(url) 
.then(function(response) {
/* successFunction */
}, function(err) {
/* errorFunction */
})

.then
Обробка помилок
Так як обидва параметра (successFunction і errorFunction) опціональні, ми можемо розділити їх на два
.then()
для кращої читаності.
get(url) 
.then(function(response) {
/* successFunction */
}, undefined)
.then(undefined, function(err) {
/* errorFunction */
})

Що б зробити код ще більш зрозумілим, ми можемо використовувати
.catch()
метод, який є скороченим варіантом для
.then(undefined, errorFunction)

get(url) 
.then(function(response) {
/* successFunction */
})
.catch(function(err) {
/* errorFunction */
})

.catch
Формування ланцюга
Справжня цінність промисов полягає в тому, що ми можемо виконувати кілька асинхронних функцій по порядку. Ми можемо об'єднати
.then()
та
.catch()
разом для створення послідовності асинхронних функцій.
Ми можемо зробити це, повертаючи ще один промис після виконання або відхилення попереднього. Наприклад -
get(url) 
.then(function(response) {
response = JSON.parse(response);
var secondURL = response.data.url
return get( secondURL ); /* Повертаємо новий промис */
})
.then(function(response) {
response = JSON.parse(response);
var thirdURL = response.data.url
return get( thirdURL ); /* Повертаємо новий промис */
})
.catch(function(err) {
handleError(err);
});

Якщо промис виконаний(resolved), то викличеться найближчий
.then()
в послідовності. Якщо промис відхилено(rejected), то найближчий
.catch()
в послідовності.
chaining
Паралельний виконання промисов
Може виникнути ситуація, коли нам знадобиться виконати кілька промисов паралельно, і продовжувати алгоритм тільки після того, як всі промисы будуть виконані. Наприклад, якщо ми хочемо отримати ряд зображень і тільки після цього відобразити їх на сторінці.
Щоб це зробити, нам необхідно використовувати два методу. Це
Array.map()
, для того, що б застосувати промис для кожного елемента масиву і зберегти результат в новий масив. І
Promise.all()
, який виконає
resolve()
у разі виконання всіх промисов в масиві. Якщо хоч один промис в масиві буде відхилено,
Promise.all()
теж буде відхилено.
var arrayOfURLs = ['one.json', 'two.json', 'three.json', 'four.json']; 
var arrayOfPromises = arrayOfURLs.map(get);

Promise.all(arrayOfPromises) 
.then(function(arrayOfResults) {
/* Зробити що-небудь, коли всі промисы в масиві зарезолвятся */
})
.catch(function(err) {
/* Виконується, якщо хоч один промис в масиві відхилений */
})

Promise.all
Якщо ми подивимося в мережеву панель (Network panel) інструментів розробки (Development tools), ми побачимо, що всі запити трапляються паралельно.
Development Tools
Якщо вам потрібна підтримка IE і/або Opera Mini, використовуйте полифил.
Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.