JavaScript Promise - результат операції, яка ще не завершена, але буде в якийсь невизначений момент в майбутньому. Прикладом такої операції є мережевий запит. Коли ми витягуємо дані з деякого джерела, наприклад з API, для нас немає ніякого способу абсолютно визначити, коли буде отримано відповідь.
Це може бути проблематичним, якщо у нас є інші операції, що залежать від завершення цього мережевого запиту. Без Promises, ми повинні використовувати зворотні виклики для визначення дій, які повинні відбутися в певній послідовності. Це не обов'язково стане проблемою, якщо ми маємо тільки одну асинхронну дію. Але якщо нам потрібно завершити декілька асинхронних дій послідовно, зворотні виклики стають некерованими і призведуть до сумнозвісного 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); }
// Complete
}); // end doFinalThing
}); // end doMoreStuff
}); // end doSomethingElse
}); // end doSomething
Promises забезпечують стандартизований і чистий метод визначення задач, які потрібно реалізувати в певній послідовності.
doSomething()
.then(doSomethingElse)
.catch(handleError)
.then(doMoreStuff)
.then(doFinalThing)
.catch(handleAnotherError)
Створення Promises
Promise створюється за допомогою Promise конструктору. Приймається функція з двома аргументи (resolve, reject)
в якості єдиного параметра.
var promise = new Promise( function(resolve, reject) { /* Promise content */ }
{full-post-img}
В середині функції ми можемо виконати будь-які асинхронні задачі, які ми хочемо. Для позначення Promise як виконана, ми викликаємо resolve()
, необов'язково передаючи значення яке ми хочемо повернути. Для позначення Promise як відхилена або провалена, ми викликаємо reject()
, при цьому необов'язково передаючи повідомлення про помилку. Перед тим, як Promise виконана або відхилена, вона знаходиться в стані очікування.
Ось загальна Promise-ified версія 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); /* PROMISE RESOLVED */
} else {
reject(Error(req.statusText)); /* PROMISE REJECTED */
}
};
req.onerror = function() { reject(Error("Network Error")); };
req.send();
});
}
Використання Promises
Після того, як ми створили Promise, ми повинні реально використовувати її. Для виконання promise-ified функції, ми можемо викликати її як і будь-яку звичайну функцію. Але, оскільки це promise, ми тепер маємо доступ до методу .then ()
, який ми можемо додати до функції і який буде виконаний, коли promise більше не знаходиться на розгляді.
Метод .then ()
приймає два необов'язкові параметри. По-перше, функція викликається, якщо promise виконано. По-друге, функція викликається, якщо promise буде відхилено.
get(url)
.then(function(response) {
/* successFunction */
}, function(err) {
/* errorFunction */
})
{full-post-img}
Обробка помилок
Оскільки обидві, успішна і помилкова, функції є необов'язковими, ми можемо розділити їх на дві .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 */
})
{full-post-img}
Формування послідовності
Реальне значення в промісах, коли ми маємо декілька асинхронних функцій які нам потрібно виконати в певному порядку. Ми можемо зв'язати .then()
і .catch()
разом, щоб створити послідовність асинхронних функцій.
Ми робимо це, повертаючи іншу promise в межах успішної чи помилкової функції. Наприклад -
get(url)
.then(function(response) {
response = JSON.parse(response);
var secondURL = response.data.url
return get( secondURL ); /* Return another Promise */
})
.then(function(response) {
response = JSON.parse(response);
var thirdURL = response.data.url
return get( thirdURL ); /* Return another Promise */
})
.catch(function(err) {
handleError(err);
});
Якщо promise з ланцюжка вирішена, вона буде рухатися далі до наступної успішної функції (.then() )
в послідовності. Якщо, з іншого боку, promise відхилена, вона перейде до наступної помилкової функція (.catch())
в послідовності.
Асинхронне виконання promises
Можуть бути випадки, коли ми хочемо виконати групу promise-ified функцій асинхронно, і потів виконати дію тільки тоді, коли всі promises будуть завершені. Наприклад, якщо ми хочемо перенести декілька зображень і відображати їх на сторінці.
Для цього нам потрібно використати два методи. Перший - метод Array.map()
дозволяє нам виконати дію для кожного елемента масиву, і створює новий масив результатів цих дій.
Другий - метод Promise.all()
повертає promise, який виконано тільки тоді коли всі проміси з масиву будуть вирішені. Якщо який-небудь один promise в межах масиву відхиляється, Promise.all ()
promise також відхиляється.
var arrayOfURLs = ['one.json', 'two.json', 'three.json', 'four.json'];
var arrayOfPromises = arrayOfURLs.map(get);
Promise.all(arrayOfPromises)
.then(function(arrayOfResults) {
/* Do something when all Promises are resolved */
})
.catch(function(err) {
/* Handle error is any of Promises fails */
})
{full-post-img}
Якщо ми подивимося на панелі Network в наших інструментах розробки, ми можемо бачити, що всі вибрані запити відбуваються паралельно.
{full-post-img}
Ще немає коментарів