Так, обіцянки - це асинхронні зворотні дзвінки. Вони не можуть зробити нічого, чого зворотний виклик не може зробити, і ви стикаєтесь з тими ж проблемами з асинхронністю, що і з простими зворотними викликами.
Однак обіцянки - це не просто зворотній зв'язок. Вони є дуже потужною абстракцією, дозволяють отримати більш чистий та кращий функціональний код із меншою кількістю схильних до помилок котельних плит.
То яка головна ідея?
Обіцянки - це об'єкти, що представляють результат одного (асинхронного) обчислення. Вони вирішують цей результат лише один раз. Є кілька речей, що це означає:
Обіцяє застосовувати схему спостерігачів:
- Вам не потрібно знати зворотні дзвінки, які використовуватимуть значення до завершення завдання.
- Замість того, щоб очікувати зворотних дзвінків як аргументів вашим функціям, ви можете легко
return
об'єкт Обіцяти
- Обіцянка збереже значення, і ви можете прозоро додавати зворотний дзвінок, коли захочете. Він буде викликаний, коли результат буде доступний. "Прозорість" означає, що коли у вас є обіцянка та додавання зворотного дзвінка до неї, це не має значення для вашого коду, чи результат вже прийшов - API та контракти однакові, значно спрощуючи кешування / запам'ятовування.
- Ви можете легко додати кілька зворотних дзвінків
Обіцянки в ланцюжку ( Монадический , якщо ви хочете ):
- Якщо вам необхідно перетворити значення , що обіцянка представляє, ви карту функції перетворення над обіцянкою і повернути нову обіцянку , яке представляє перетворений результат. Ви не можете синхронно отримати значення, щоб якось його використовувати, але ви можете легко підняти перетворення в контексті обіцянки. Немає зворотних дзвінків на котлах.
- Якщо ви хочете з'єднати дві асинхронні задачі, ви можете скористатися
.then()
методом. Буде потрібен зворотний дзвінок, який буде викликаний з першим результатом, і поверне обіцянку за результат обіцянки, що повернення дзвінка повернеться.
Звучить складно? Час прикладу коду.
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
var p2 = api2(); // returning a promise
return p2; // The result of p2 …
}); // … becomes the result of p3
// So it does not make a difference whether you write
api1().then(function(api1Result) {
return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
return api2();
}).then(console.log)
Плоскість не виходить чарівно, але ви можете це легко зробити. Для вашого сильно вкладеного прикладу, (близький) еквівалент був би
api1().then(api2).then(api3).then(/* do-work-callback */);
Якщо бачення коду цих методів допомагає зрозуміти, ось найосновніший пробіл із обіцянками в декількох рядках .
Яка велика метушня щодо обіцянок?
Абстракція «Обіцяння» дозволяє значно краще поєднувати функції. Наприклад, поруч із then
ланцюжком all
функція створює обіцянку для комбінованого результату декількох обіцянок паралельно очікування.
Останнє, але не менш важливе, обіцянки мають інтегровану обробку помилок. Результатом обчислень може бути те, що або обіцянка виконується зі значенням, або вона відкидається з причиною. Усі функції композиції справляються з цим автоматично і розповсюджують помилки в ланцюгах обіцянок, так що вам не потрібно про це піклуватися явно скрізь - на відміну від простої реалізації зворотного виклику. Зрештою, ви можете додати спеціальний зворотний виклик помилок для всіх винятків, що відбулися.
Не кажучи вже про необхідність перетворювати речі на обіцянки.
Це досить тривіально, з хорошими бібліотеками з обіцянками, див. Як перетворити існуючий API зворотного виклику в обіцянки?