В чем разница между будущим и обещанием?

275

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

user1170330
источник
100
Вы можете сделать, Promiseи это зависит от вас, чтобы сохранить его. Когда кто-то еще дает вам обещание, вы должны подождать, чтобы увидеть, соблюдают ли они его вFuture
Кевин Райт
30
Одна из наименее полезных статей Википедии, которые я когда-либо читал
Fulluphigh

Ответы:

146

Согласно этому обсуждению , 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);
assylias
источник
78
Это не твоя вина, Ассилиас, но этот экстракт javadoc нуждается в серьезной доработке со стороны приличного технического автора. На пятом прочтении я могу начать ценить то, что он пытается сказать ... и я подхожу к этому с пониманием будущего и обещаний, которые уже существуют!
Свекла-свекла
2
@ Beetroot-Beetroot, похоже, уже произошло.
Герман
1
@ herman Спасибо - я обновил ссылку, чтобы указать на окончательную версию Javadoc.
assylias
7
@ Beetroot-Beetroot Вы должны проверить документ для метода Исключительно. Это было бы замечательное стихотворение, но это исключительная ошибка читаемой документации.
Fulluphigh
4
Для всех, кто интересуется, @Fulluphigh ссылается на это . Похоже, это было удалено / пересмотрено в Java 8.
Седрик Райхенбах
148

(Пока я не совсем доволен ответами, так что моя попытка ...)

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

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

lbalazscs
источник
116

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

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

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

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

Рахель Люти
источник
1
Означает ли это, что Обещание всегда является подклассом Future, а возможность записи Future просто скрыта типом?
devios1
Я не думаю, что это подразумевается . Хотя в реализации это часто бывает (например, в Java, Scala).
Рахель Люти,
74

Я приведу пример того, что такое 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), а папа не дома, а только мама с ее «медленным кошельком», то ее обещание будет выполнено только в том случае, если программа живет дольше, чем мама должна получить деньги от кошелек.

Исполнитель по умолчанию действует как «демон» и не ожидает выполнения всех обещаний. Я не нашел хорошего описания этого факта ...

Владимир Набоков
источник
8
Это так весело читать! Я не думаю, что смогу забыть о будущем и обещать больше.
user1532146
2
Это должно быть принято как ответ. Это как чтение истории. Спасибо @Vladimir
Филлен
Спасибо @Vladimir
intvprep
9

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

Например, посмотрите, как определяются обещания в 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-await, но async-await просто удаляет шаблон .... затем (function (result) {.... from it).

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

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

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

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

Бодрин
источник
4
+1. Это правильный ответ на этот вопрос. CompletableFutureможет иметь некоторое сходство с a, Promiseно это все же не такPromise , потому что способ, которым он предназначен для использования, отличается: результат a Promiseпотребляется при вызове then(function), а функция выполняется в контексте производителя сразу после вызова производителя resolve, FutureРезультат A потребляется вызовом, getкоторый заставляет поток потребителя ждать, пока поток производителя не сгенерирует значение, а затем обрабатывает его в потребителе. Futureпо своей сути многопоточный, но ...
Периата Breatta
5
... вполне возможно использовать a Promiseтолько с одним потоком (и фактически это именно та среда, для которой они были изначально разработаны: приложения javascript обычно имеют только один поток, поэтому вы не можете реализовать их Futureтам). Promiseпоэтому он намного легче и эффективнее Future, но Futureможет быть полезен в ситуациях, которые являются более сложными и требуют взаимодействия между потоками, которые не могут быть легко организованы с помощью Promises. Подводя итог: Promiseэто модель push, в то время Futureкак модель pull (ср. Iterable против Observable)
Periata Breatta
@PeriataBreatta Даже в однопоточной среде должно быть что-то, выполняющее обещание (которое обычно работает как другой поток, например, an XMLHttpRequest). Я не верю утверждению эффективности, у вас есть какие-то цифры? +++ При этом очень хорошее объяснение.
Maaartinus
1
@maaartinus - да, что-то должно выполнить обещание, но это может (и на самом деле во многих случаях это) сделать с помощью цикла верхнего уровня, который запрашивает изменения во внешнем состоянии и разрешает любые обещания, относящиеся к выполненным действиям. С точки зрения эффективности, у меня нет точных данных по Обещаниям конкретно, но учтите, что вызов getнеразрешенного решения Futureобязательно потребует двухпоточных переключений контекста, что, по крайней мере, несколько лет назад могло потребовать около 50 человек .
Периата Breatta
@PeriataBreatta На самом деле ваш комментарий должен быть принятым решением. Я искал объяснение (тянуть / толкать, одно / многопоточность), как у вас.
Томас Джейкоб
5

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

user2562234
источник
2

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

Jacky
источник