Разница между будущим и обещанием

Какая разница между Future и Promise?
Они оба действуют как заполнитель для будущих результатов, но где основное различие?

Ответ 1

В соответствии с этим обсуждением Promise, наконец, был вызван CompletableFuture для включения в Java 8 и его javadoc объясняет:

A Будущее, которое может быть явно завершено (установка его значения и статуса) и может использоваться как CompletionStage, поддерживая зависимые функции и действия, которые запускаются после его завершения.

Пример также указан в списке:

f.then((s -> aStringFunction(s)).thenAsync(s -> ...);

Обратите внимание, что конечный API немного отличается, но позволяет подобное асинхронное выполнение:

CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);

Ответ 2

(Я до сих пор не доволен ответами, поэтому вот моя попытка...)

Я думаю, что комментарий Кевина Райт ( "Вы можете сделать обещание, и это зависит от вас, чтобы сохранить его. Когда кто-то сделает вам обещание, вы должны подождать, чтобы удостовериться, что они почитают его в будущем" ) резюмирует его довольно хорошо, но может быть полезно некоторое объяснение.

Фьючерсы и promises - довольно похожие понятия, разница в том, что будущее является контейнером только для чтения, для результата, которого еще нет, а обещание может быть написано (обычно только один раз). Java 8 CompletableFuture и Guava SettableFuture можно охарактеризовать как promises, потому что их значение могут быть установлены ( "завершены" ), но они также реализуют интерфейс Future, поэтому для клиента нет никакой разницы.

Результат будущего будет задан "кем-то другим" - результатом асинхронного вычисления. Обратите внимание, что FutureTask - классическое будущее - должно быть инициализировано с помощью Callable или Runnable, нет конструктора без аргументов, и как Future, так и FutureTask доступны только для чтения извне (заданные методы FutureTask защищены). Значение будет установлено на результат вычисления изнутри.

С другой стороны, результат обещания может быть задан "вы" (или фактически кем-либо) в любое время, потому что у него есть общедоступный метод setter. Оба CompletableFuture и SettableFuture могут быть созданы без какой-либо задачи, и их значение может быть установлено в любое время. Вы отправляете обещание на код клиента и выполняете его позже, как пожелаете.

Обратите внимание, что CompletableFuture не является "чистым" обещанием, его можно инициализировать с помощью задачи, аналогичной FutureTask, и ее наиболее полезной функцией является несвязанная цепочка шагов обработки.

Также обратите внимание, что обещание не обязательно должно быть подтипом будущего, и он не должен быть одним и тем же объектом. В Scala объект Future создается асинхронным вычислением или другим объектом Promise. В С++ ситуация аналогична: объект обещания используется производителем и будущим объектом потребителем. Преимущество этого разделения заключается в том, что клиент не может установить ценность будущего.

Оба Spring и EJB 3.1 имеют класс AsyncResult, который похож на Scala/С++ promises. AsyncResult реализует Future, но это не настоящее будущее: асинхронные методы в Spring/EJB возвращают другой объект Future-only для чтения с помощью некоторой фоновой магии, и это второе "реальное" будущее может использоваться клиентом для доступа к результат.

Ответ 3

Я знаю, что уже есть принятый ответ, но хотелось бы добавить мои два цента:

Как вызывающий метод асинхронного API, вы получите Future как дескриптор результата вычисления. Вы можете, например, вызовите get(), чтобы дождаться завершения вычисления и получения результата.

Теперь подумайте, как этот метод API фактически реализован: разработчик должен немедленно вернуть Future. Она отвечает за завершение этого будущего, как только будет сделано вычисление (которое она будет знать, потому что она реализует логику отправки;-)). Она будет использовать Promise/ CompletableFuture, чтобы сделать именно это: немедленно создать и вернуть CompletableFuture и вызвать complete(T result) после выполнения вычисления.

TL;DR: будущее и обещание - это две стороны асинхронной операции: потребитель/вызывающий и производитель/разработчик.

Ответ 4

Я приведу пример того, что такое обещание, и как его значение может быть установлено в любое время, в противоположность Будущему, значение которого доступно только для чтения.

Предположим, у вас есть мама, и вы просите у нее денег.

Теперь вы обманываете свою маму, создавая вам обещание возможного пожертвования, она дает вам этот объект обещания, но она не очень опрометчиво, чтобы выполнить ее еще:

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), а отца нет дома, только мама с ее "медленным кошельком", то ее обещание будет завершено, если в программе больше времени, чем маме, нужно получить деньги от Кошелек. Я имею в виду, что исполнитель по умолчанию действует как "демон". Я не нашел хорошего описания этого факта...

Ответ 5

Не уверен, что это может быть ответ, но, поскольку я вижу, что другие сказали для кого-то, может показаться, что вам нужны две отдельные абстракции для обеих этих концепций, так что один из них (Future) является просто средством чтения- только вид другого (Promise)... но на самом деле это не требуется.

Например, посмотрите, как promises определены в javascript:

https://promisesaplus.com/

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-wait, но async-wait просто удаляет шаблон... затем (функция (результат) {.... от него).

И на самом деле их абстракция довольно хороша, как конструктор обещаний

new Promise( function(resolve, reject) { /* do it */ } );

позволяет вам предоставить два обратных вызова, которые могут быть использованы для успешного завершения Promise или с ошибкой. Так что только код, который создает Promise, может завершить его, а код, который получает уже построенный объект Promise, имеет вид только для чтения.

С наследованием вышесказанное может быть достигнуто, если методы разрешения и отклонения защищены.

Ответ 6

Отсутствует установленный метод в интерфейсе Future, только получите метод, поэтому он доступен только для чтения. О CompletableFuture, эта статья может быть полезной. completablefuture

Ответ 7

Для клиентского кода Promise предназначена для наблюдения или привязки обратного вызова, когда результат доступен, тогда как Future должен ждать результата, а затем продолжить. Теоретически все, что можно сделать с фьючерсами, что можно сделать с помощью promises, но из-за различий в стиле результирующий API для promises на разных языках упрощает цепочку.