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

В чем разница между 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://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 Объект имеет вид только для чтения.

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

Для клиентского кода 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));
Другие вопросы по тегам