web-dev-qa-db-ja.com

未来と約束の違いは何ですか?

FuturePromiseの違いは何ですか?
どちらも将来の結果のプレースホルダーのように機能しますが、主な違いはどこですか?

203
user1170330

この議論 に従って、PromiseはついにJava 8に含めるためにCompletableFutureと呼ばれ、 そのjavadoc は次のように説明しています。

明示的に完了させることができ(その値とステータスを設定し)、完了時にトリガーされる依存関数およびアクションをサポートする、CompletionStageとして使用することができる未来。

例もリストにあります。

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

最終的なAPIは少し異なりますが、同様の非同期実行が可能です。

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

(私は今のところ答えに完全に満足していないので、ここに私の試みがあります...)

Kevin Wrightのコメント(あなたは約束をすることができ、それを守るのはあなた次第です。他の誰かがあなたに約束をするとき、あなたは彼らが将来それを守るかどうかを確かめるために待たなければなりません])それをかなりよく要約しています、しかし、いくらかの説明は役に立つかもしれません。

未来と約束 はかなり似た概念ですが、違いは約束は書き込めますが(通常は一度だけ)、未来はまだ存在しない結果のための読み取り専用コンテナです。 Java 8 CompletableFuture とGuava SettableFuture は、それらの値を設定( "完了")できるので、見込みと見なすことができますが、Futureインターフェースも実装しています。クライアントに違いはありません。

将来の結果は「他の誰か」によって、非同期計算の結果によって設定されます。 FutureTask - 古典的な未来 - mustはCallableまたはRunnableで初期化され、引数なしのコンストラクタはなく、FutureとFutureTaskはどちらも読み取り専用です。 outside(FutureTaskのsetメソッドは保護されています)値は内側からの計算結果に設定されます。

一方、promiseの結果はpublic setterメソッドを持っているのでいつでも "あなた"(または実際には誰でも)によって設定することができます。 CompletableFutureとSettableFutureはタスクなしで作成でき、それらの値はいつでも設定できます。あなたは約束をクライアントコードに送り、そしてあなたが望むように後でそれを果たす。

CompletableFutureは「純粋な」約束ではないことに注意してください、それはFutureTaskのようにタスクで初期化することができ、そしてその最も有用な機能は無関係な処理ステップの連鎖です。

また、約束は将来のサブタイプである必要はなく、また同じオブジェクトである必要もありません。 Scalaでは、Futureオブジェクトは非同期計算またはdifferent Promiseオブジェクトによって作成されます。 C++でも状況は似ています。promiseオブジェクトはプロデューサによって使用され、futureオブジェクトはコンシューマによって使用されます。この分離の利点は、クライアントが未来の価値を設定できないことです。

SpringEJB 3.1 の両方にAsyncResultクラスがあります。これはScala/C++の約束に似ています。 AsyncResultはFutureを実装していますが、これは実際の未来ではありません。SSpring/EJBの非同期メソッドは、バックグラウンドマジックを通じて異なる読み取り専用のFutureオブジェクトを返します。

114
lbalazscs

私はすでに受け入れられた答えがあることを知っています、それでも私の2セントを加えたいと思います:

TLDR:FutureとPromiseは非同期操作の2つの側面です。消費者/呼び出し元 vs. 生産者/実装者

非同期APIメソッドの呼び出し元として、計算結果へのハンドルとしてFutureを取得します。できます。その上でget()を呼び出して、計算が完了して結果を取得するのを待ちます。

このAPIメソッドが実際にどのように実装されているかを考えてみましょう。実装者はすぐにFutureを返さなければなりません。彼らは計算が行われるとすぐにその未来を完成させる責任があります(それはそれがディスパッチ論理を実装しているので知っています;-))。そのためには、Promise/CompletableFutureを使用します。すぐにCompletableFutureを作成して返し、計算が完了したらcomplete(T result)を呼び出します。

91
Rahel Lüthy

将来とは反対に、Promiseとは何か、およびその値をいつでも設定できる方法の例を示します。この値は読み取り専用です。

あなたがお母さんを持っていて、あなたが彼女にお金を頼むとしましょう。

今、あなたはあなたのお母さんをあなたに最終的な寄付の約束を作成させることに騙します、彼女はあなたにその約束の目的をあなたに与えます、しかし、彼女はまだそれを達成するために発疹にいません:

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);

私が明示的に書いたエグゼキュータに気づいたことがありますか?興味深いことに、代わりにデフォルトの暗黙的なexecutor(commonPool)を使用していて、父親が家にいない場合、彼女の「遅い財布」を持つお母さんだけがそうです。財布。私は、デフォルトのexecutorが "daemon"のように振る舞うことを意味します。私はこの事実の良い説明を見つけていません...

39

これが答えになるかどうかはわかりませんが、他の人が誰かに言ったことを見ると、これらの概念の両方に2つの別々の抽象化が必要なように見えるので、そのうちの1つ(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は単に削除します定型文.... then(function(result){....から)。

そして実際、それらの抽象化はプロミスコンストラクタとしてかなり良いです

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

Promiseを正常に完了するか、エラーを発生させるために使用できる2つのコールバックを提供できます。そのため、Promiseを構築するコードのみがそれを完了でき、既に構築されたPromiseオブジェクトを受け取るコードには読み取り専用ビューがあります。

継承により、resolveおよびrejectが保護されたメソッドである場合、上記を実現できます。

7
bodrin

クライアントコードの場合、Promiseは結果が利用可能になったときにコールバックを監視またはアタッチするためのものですが、Futureは結果を待ってから続行するためのものです。理論的には、promiseを使ってできることが未来にできることは何でも可能ですが、スタイルの違いのために、異なる言語でのpromiseの結果のAPIは連鎖を容易にします。

2
user2562234

Futureインタフェースにはsetメソッドはなく、getメソッドだけなので読み取り専用です。 CompletableFutureについて、この記事は役に立つかもしれません。 補完

2
Jacky