Яка різниця між Future
і Promise
?
Вони обидва виступають як заповнювач для майбутніх результатів, але де головна відмінність?
Яка різниця між Future
і Promise
?
Вони обидва виступають як заповнювач для майбутніх результатів, але де головна відмінність?
Відповіді:
Відповідно до цієї дискусії , Promise
нарешті було закликано CompletableFuture
включити в Java 8, і її javadoc пояснює:
Майбутнє, яке може бути явно завершене (встановлюючи його значення та статус) і може використовуватися як CompletionStage, підтримуючи залежні функції та дії, що запускаються після його завершення.
Приклад також наведено у списку:
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
Зауважте, що кінцевий API дещо відрізняється, але дозволяє подібне асинхронне виконання:
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(Я поки що не задоволений відповідями, тому ось моя спроба ...)
Я думаю, що коментар Кевіна Райт ( "Ви можете пообіцяти, і ви дотримуєтесь цього. Коли хтось дасть вам обіцянку, ви повинні почекати, щоб вони шанували це у майбутньому" ) це підсумовує досить добре, але деякі пояснення може бути корисним.
Майбутні і обіцянки є досить схожими поняттями, різниця полягає в тому, що майбутнє - це контейнер, доступний лише для читання, для результату, який ще не існує, тоді як обіцянку можна написати (як правило, лише один раз). Java 8 CompletableFuture та Guava SettableFuture можна вважати обіцянками, оскільки їх значення можна встановити ("завершено"), але вони також реалізують інтерфейс Future, тому для клієнта різниці немає.
Результат майбутнього буде встановлений «кимось іншим» - результатом асинхронного обчислення. Зауважте, як FutureTask - класичне майбутнє - має бути ініціалізовано за допомогою Callable або Runnable, не існує конструктора без аргументів, і Future і FutureTask читаються лише зовні (встановлені методи FutureTask захищені). Значення буде встановлено в результаті обчислення зсередини.
З іншого боку, результат обіцянки може бути встановлений "ви" (або насправді ким-небудь) будь-коли, оскільки він має публічний метод встановлення. І CompletableFuture, і SettableFuture можна створити без будь-якого завдання, і їх значення можна встановити в будь-який час. Ви відправляєте обіцянку до клієнтського коду та виконуєте його пізніше, як хочете.
Зауважте, що CompletableFuture не є "чистою" обіцянкою, її можна ініціалізувати із завданням так само, як FutureTask, а його найкориснішою особливістю є непов'язане ланцюжок кроків обробки.
Також зауважте, що обіцянка не повинна бути підтипом майбутнього, і вона не повинна бути тим самим об’єктом. В Scala a Future об’єкт створюється асинхронним обчисленням або іншим об'єктом Promise. У C ++ ситуація схожа: об'єкт обіцянки використовується виробником, а майбутній об’єкт споживачем. Перевага цього розділення полягає в тому, що клієнт не може встановити значення майбутнього.
І Spring, і EJB 3.1 мають клас AsyncResult, схожий на обіцянки Scala / C ++. AsyncResult реалізує майбутнє, але це не справжнє майбутнє: асинхронні методи у Spring / EJB повертають інший об'єкт майбутнього, доступний лише для читання, завдяки деякій фоновій магії, і це друге "реальне" майбутнє може бути використане клієнтом для отримання результату.
Я усвідомлюю, що відповідь вже прийнята, але хотів би додати свої два центи:
TLDR: Майбутнє та Обіцяння - це дві сторони асинхронної операції: споживач / абонент та виробник / виконавець .
Як викликає асинхронного метод API, ви отримаєте в Future
якості ручки для результату обчислення в. Наприклад, ви можете зателефонувати get()
на нього, щоб дочекатися завершення обчислення та отримання результату.
Тепер подумайте, як реально реалізується цей метод API: Реалізатор повинен Future
негайно повернутися . Вони несуть відповідальність за завершення цього майбутнього, як тільки буде проведено обчислення (про що вони будуть знати, оскільки він реалізує логіку відправлення ;-)). Вони використовуватимуть a Promise
/ CompletableFuture
to робити саме так: Побудуйте та поверніть CompletableFuture
негайно та зателефонуйте, complete(T result)
коли обчислення виконано.
Я наведу приклад того, що таке Promise і як її значення можна було встановити в будь-який момент, на відміну від Future, яке значення є лише читабельним.
Припустимо, у вас є мама, і ви просите у неї грошей.
// Now , you trick your mom into creating you a promise of eventual
// donation, she gives you that promise object, but she is not really
// in rush to fulfill it yet:
Supplier<Integer> momsPurse = ()-> {
try {
Thread.sleep(1000);//mom is busy
} catch (InterruptedException e) {
;
}
return 100;
};
ExecutorService ex = Executors.newFixedThreadPool(10);
CompletableFuture<Integer> promise =
CompletableFuture.supplyAsync(momsPurse, ex);
// You are happy, you run to thank you your mom:
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));
// But your father interferes and generally aborts mom's plans and
// completes the promise (sets its value!) with far lesser contribution,
// as fathers do, very resolutely, while mom is slowly opening her purse
// (remember the Thread.sleep(...)) :
promise.complete(10);
Результатом цього є:
Thank you mom for $10
Обіцянка мами була створена, але чекала на якусь подію "завершення".
CompletableFuture<Integer> promise...
Ви створили таку подію, прийнявши її обіцянку та оголосивши свої плани подякувати мамі:
promise.thenAccept...
У цей момент мама почала відкривати гаманець ... але дуже повільно ...
а батько втрутився набагато швидше та виконав обіцянку замість вашої мами:
promise.complete(10);
Ви помітили виконавця, про який я писав прямо?
Цікаво, що якщо ви замість цього використовуєте неявний виконавець за замовчуванням (commonPool), а батька немає вдома, а лише мама зі своїм «повільним гаманцем», то її обіцянка виконається лише в тому випадку, якщо програма живе довше, ніж мамі потрібно отримати гроші від гаманець.
Виконавець за замовчуванням діє на зразок "демона" і не чекає, коли всі обіцянки будуть виконані. Я не знайшов хорошого опису цього факту ...
Не впевнений, що це може бути відповіддю, але, як я бачу, що хтось сказав для когось, це може виглядати так, що вам потрібні дві окремі абстракції для обох цих понять, так що одна з них ( Future
) - це лише перегляд другого для читання ( Promise
) ... але насправді це не потрібно.
Наприклад, подивіться, як визначаються обіцянки в javascript:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Основна увага приділяється компостуванню за допомогою такого then
методу:
asyncOp1()
.then(function(op1Result){
// do something
return asyncOp2();
})
.then(function(op2Result){
// do something more
return asyncOp3();
})
.then(function(op3Result){
// do something even more
return syncOp4(op3Result);
})
...
.then(function(result){
console.log(result);
})
.catch(function(error){
console.log(error);
})
що робить асинхронні обчислення схожими на синхронні:
try {
op1Result = syncOp1();
// do something
op1Result = syncOp2();
// do something more
op3Result = syncOp3();
// do something even more
syncOp4(op3Result);
...
console.log(result);
} catch(error) {
console.log(error);
}
що досить класно. (Не настільки круто, як async-await, але async-await просто видаляє котельну панель .... тоді (функція (результат) {.... з неї).
І насправді їх абстрагування досить добре, як конструктор обіцянок
new Promise( function(resolve, reject) { /* do it */ } );
дозволяє надати два зворотні виклики, які можна використати для Promise
успішного або помилкового завершення. Так що лише код, який будує, Promise
може завершити його, і код, який отримує вже сконструйований Promise
об'єкт, має вигляд лише для читання.
З успадкуванням вищезазначеного можна досягти, якщо способи вирішення та відхилення є захищеними методами.
CompletableFuture
може мати деяку схожість з Promise
але, але це все ще не єPromise
, тому що спосіб його споживання інший: Promise
результат вживається за допомогою виклику then(function)
, а функція виконується в контексті виробника відразу після виклику виробника resolve
. Future
Результат «s споживається викликом , get
який викликає споживчу нитка чекати , поки виробник потік не генерується значення, а потім обробляє його в споживача. Future
за своєю суттю багатопоточне, але ...
Promise
лише один потік (а насправді це саме те середовище, для якого вони були спочатку розроблені: програми javascript зазвичай мають лише один потік, тому ви не можетеFuture
там їх реалізувати ). Promise
Тому набагато легше і ефективніше Future
, але Future
може бути корисним у складних ситуаціях та потребує співпраці між потоками, які неможливо легко впорядкувати за допомогою Promise
s. Підводячи підсумок: Promise
це Future
поштова модель, а модель - тяга (пор. Iterable vs Observable)
XMLHttpRequest
). Я не вірю в твердження про ефективність, чи трапляються у вас якісь цифри? +++ Це сказало, дуже приємне пояснення.
get
невирішених Future
обов'язково будуть входити 2 потокові контекстні комутатори, що, принаймні, через кілька років тому, ймовірно, вимагатиме близько 50 нас .
Щодо коду клієнта, Promise - це спостереження або додавання зворотного дзвінка, коли результат доступний, тоді як майбутнє - це чекати результату, а потім продовжувати. Теоретично все, що можна зробити з ф'ючерсами, що можна зробити з обіцянками, але через різницю стилів, отриманий API для обіцянок на різних мовах робить ланцюжок простішим.
У інтерфейсі майбутнього немає встановленого методу, отримуйте лише метод, тому він доступний лише для читання. Про CompletableFuture, ця стаття може бути корисною. повноцінне майбутнє
Promise
і ви дотримуєтесь його. Коли хтось дасть вам обіцянку, ви повинні дочекатися, щоб побачити, чи будуть вони її шануватиFuture