В чем разница между будущим и обещанием?
В чем разница между Future
а также Promise
?
Они оба действуют как заполнитель для будущих результатов, но где главное отличие?
9 ответов
Согласно этой дискуссии, 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, конструктора без аргументов нет, а FutureTask и FutureTask доступны только для чтения извне (методы набора FutureTask защищены). Значение будет установлено на результат вычисления изнутри.
С другой стороны, результат обещания может быть установлен "вами" (или фактически кем угодно) в любое время, потому что у него есть метод открытого сеттера. И CompletableFuture, и SettableFuture могут быть созданы без каких-либо задач, и их значение может быть установлено в любое время. Вы отправляете обещание клиентскому коду и выполняете его позже, как пожелаете.
Обратите внимание, что CompletableFuture не является "чистым" обещанием, его можно инициализировать с помощью задачи, аналогичной FutureTask, и его наиболее полезная функция - это не связанная цепочка этапов обработки.
Также обратите внимание, что обещание не обязательно должно быть подтипом будущего и не обязательно должно быть одним и тем же объектом. В Scala объект Future создается асинхронным вычислением или другим объектом Promise. В C++ ситуация аналогична: объект обещания используется производителем, а объект будущего - потребителем. Преимущество такого разделения заключается в том, что клиент не может установить ценность будущего.
Как Spring, так и EJB 3.1 имеют класс AsyncResult, который похож на обещания Scala/C++. AsyncResult реализует Future, но это не настоящее будущее: асинхронные методы в Spring/EJB возвращают другой объект Future, доступный только для чтения, посредством некоторой фоновой магии, и это второе "реальное" будущее может использоваться клиентом для доступа к результату.
Я знаю, что уже есть принятый ответ, но все же хотел бы добавить свои два цента:
TLDR: будущее и обещание - это две стороны асинхронной операции: потребитель / вызывающий абонент против производителя / разработчика.
Как вызывающий метод асинхронного API, вы получите Future
как дескриптор к результату вычисления. Вы можете, например, позвонить get()
на нем, чтобы дождаться вычисления, чтобы завершить и получить результат.
Теперь подумайте о том, как на самом деле реализован этот метод API: разработчик должен вернуть Future
немедленно. Она отвечает за завершение этого будущего, как только вычисления будут выполнены (что она узнает, потому что она реализует логику диспетчеризации;-)). Она будет использовать Promise
/ CompletableFuture
сделать это: построить и вернуть CompletableFuture
немедленно и позвоните complete(T result)
как только вычисление сделано.
Я приведу пример того, что такое Promise и как его значение может быть установлено в любое время, в отличие от Future, это значение доступно только для чтения.
Предположим, у вас есть мама, и вы просите у нее денег.
Теперь, вы обманываете свою маму, чтобы создать вам обещание возможного пожертвования, она дает вам этот объект обещания, но на самом деле она еще не спешит выполнить его:
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);
Ты счастлив, ты бежишь, чтобы поблагодарить твою маму
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));
Но твой отец вмешивается и обычно прерывает планы мамы и выполняет обещание (устанавливает его ценность!) С гораздо меньшим вкладом, как делают отцы, очень решительно, в то время как мама медленно открывает свой кошелек (обратите внимание на Thread.sleep(...)):
promise.complete(10);
Выход этого:
Thank you mom for $10
Мамины обещания были созданы, но ждали какого-то "завершения" события.
CompletableFuture<Integer> promise...
Вы создали такое событие, принимая ее обещание и объявляя о своих планах поблагодарить маму:
promise.thenAccept...
В этот момент мама начала открывать сумочку... но очень медленно...
и отец вмешался намного быстрее и выполнил обещание вместо твоей мамы
promise.complete(10);
Вы заметили исполнителя, который я написал явно? Интересно, что если вместо этого вы используете неявного исполнителя по умолчанию (commonPool), а папы нет дома, только мама с ее "медленным кошельком", то ее обещание будет выполнено только в том случае, если программа проживет больше времени, чем мама должна получить деньги от Кошелек. Я имею в виду, что executor по умолчанию действует как "демон". Я не нашел хорошего описания этого факта...
Не уверен, что это может быть ответом, но, как я вижу, что другие говорили о ком-то, может показаться, что вам нужны две отдельные абстракции для обоих этих понятий, чтобы один из них (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 просто удаляет шаблон .... затем (function (result) {.... from it).
И на самом деле их абстракция довольно хороша как конструктор обещаний
new Promise( function(resolve, reject) { /* do it */ } );
позволяет вам предоставить два обратных вызова, которые можно использовать для завершения Promise
успешно или с ошибкой. Так что только код, который создает Promise
может завершить его и код, который получает уже построенный Promise
Объект имеет вид только для чтения.
С наследованием вышеупомянутое может быть достигнуто, если решить и отклонить являются защищенными методами.
Для клиентского кода Promise предназначен для наблюдения или присоединения обратного вызова, когда результат доступен, тогда как Future должен дождаться результата и затем продолжить. Теоретически все, что можно сделать с фьючерсами, что можно сделать с обещаниями, но из-за разницы в стиле, результирующий API для обещаний на разных языках облегчает создание цепочек.
и являются прокси-объектом для неизвестного результата
завершает
- чтение / потребитель неизвестного результата
- запись / производитель неизвестного результата.
//Future has a reference to Promise
Future -> Promise
Как
producer
Я что-то и отвечаю за это
Как
consumer
кто получил
promise
Я рассчитываю получить результат в
future
Что касается Java
CompletableFutures
это
Promise
потому что вы можете установить результат, а также он реализует
Future
Не задан метод в интерфейсе Future, только метод get, поэтому он доступен только для чтения. О CompletableFuture эта статья может быть полезной. completablefuture
В этом примере вы можете посмотреть, как Promises можно использовать в Java для создания асинхронных последовательностей вызовов:
doSomeProcess()
.whenResult(result -> System.out.println(String.format("Result of some process is '%s'", result)))
.whenException(e -> System.out.println(String.format("Exception after some process is '%s'", e.getMessage())))
.map(String::toLowerCase)
.mapEx((result, e) -> e == null ? String.format("The mapped result is '%s'", result) : e.getMessage())
.whenResult(s -> System.out.println(s));