回答:
この議論によると、Java 8への組み込みPromise
がついに呼ばCompletableFuture
れ、そのjavadocは次のように説明しています。
明示的に完了することができ(その値とステータスを設定)、CompletionStageとして使用できるFuture。その完了時にトリガーされる依存機能とアクションをサポートします。
リストにも例が示されています。
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
最終的なAPIは少し異なりますが、同様の非同期実行が可能です。
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(私はこれまでのところ完全に満足しているわけではないので、これが私の試みです...)
Kevin Wrightのコメント(「約束をすることができ、それを守るのはあなた次第です。他の誰かが約束をするとき、彼らが未来でそれを尊重するかどうか確認するのを待つ必要があります」)はかなりうまく要約していますが、説明が役立つ場合があります。
フューチャーとプロミスはかなり似た概念です。違いは、フューチャーはまだ存在しない結果の読み取り専用のコンテナーであり、プロミスは(通常は1回だけ)書けることです。Javaの8 CompletableFutureとグアバSettableFutureその値が(「完了」)を設定することができますので、約束と考えるが、彼らはまた、未来のインターフェイスを実装することができ、したがって、クライアントの違いはありません。
未来の結果は、「誰か他の人」-非同期計算の結果によって設定されます。FutureTask-クラシックフューチャー- が CallableまたはRunnableで初期化される必要があることに注意してください。引数のないコンストラクターはなく、FutureとFutureTaskはどちらも外部から読み取り専用です(FutureTaskのsetメソッドは保護されています)。値は内部からの計算結果に設定されます。
一方、プロミスの結果は、パブリックセッターメソッドを持っているため、「あなた」(または実際には誰でも)がいつでも設定できます。CompletableFutureとSettableFutureはどちらもタスクなしで作成でき、それらの値はいつでも設定できます。クライアントコードにプロミスを送信し、後で必要に応じてそれを実行します。
CompletableFutureは「純粋な」プロミスではないことに注意してください。FutureTaskのようなタスクで初期化でき、その最も有用な機能は、処理ステップの無関係なチェーンです。
また、promiseはfutureのサブタイプである必要はなく、同じオブジェクトである必要もないことにも注意してください。Scalaでは、Futureオブジェクトは非同期計算または別の Promiseオブジェクトによって作成されます。C ++でも状況は似ています。promiseオブジェクトはプロデューサーによって使用され、futureオブジェクトはコンシューマーによって使用されます。この分離の利点は、クライアントが将来の価値を設定できないことです。
両方のスプリングとEJB 3.1スカラ/ C ++約束と同様であるAsyncResultクラスを有します。AsyncResultはFutureを実装しますが、これは実際のフューチャーではありません。Spring/ EJBの非同期メソッドは、バックグラウンドマジックを通じて別の読み取り専用のFutureオブジェクトを返します。この2番目の「実際の」フューチャーは、クライアントが結果にアクセスするために使用できます。
私はすでに受け入れられた回答があることを認識していますが、それでも私の2セントを追加したいと思います。
TLDR:フューチャーとプロミスは非同期操作の2つの側面です:コンシューマー/呼び出し側とプロデューサー/実装者。
非同期APIメソッドの呼び出し元としてFuture
、計算結果のハンドルとしてを取得します。たとえばget()
、計算が完了するのを待って結果を取得するためにそれを呼び出すことができます。
次に、このAPIメソッドが実際にどのように実装されるかを考えてみましょう。実装者はFuture
ただちに返す必要があります。彼らは、計算が完了するとすぐにその未来を完了する責任があります(ディスパッチロジックを実装しているので、それはわかっています;-))。彼らはPromise
/ CompletableFuture
を使ってそれだけを行います:を構築してCompletableFuture
すぐに返しcomplete(T result)
、計算が完了したら呼び出します。
Promiseとは何か、その値はいつでもどのように設定できるのか、Futureとは逆に例を挙げます。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)を使用し、父親が家にいなくても、「スローパース」を持っている母親だけがいる場合、プログラムの寿命がママからのお金よりも長くなければ、彼女の約束は完了しません。財布。
デフォルトのエグゼキューターは「デーモン」のような働きをし、すべての約束が満たされるのを待ちません。私はこの事実の良い説明を見つけていません...
これが答えになるかどうかはわかりませんが、他の人が誰かに対して言ったことを見ると、これらの概念の両方に2つの別個の抽象化が必要であるように見える可能性があるため、一方(Future
)は他方の読み取り専用ビュー(Promise
)...しかし、実際にはこれは必要ありません。
たとえば、javascriptでpromiseがどのように定義されているかを見てみましょう。
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
オブジェクトを受け取るコードは読み取り専用ビューを持ちます。
継承を使用すると、解決と拒否が保護されたメソッドであれば、上記を実現できます。
CompletableFuture
いくつかの類似性を持っているかもしれませんPromise
が、それはまだありませんPromise
、消費されることを意図しています方法が異なるため:Promise
の結果を呼び出すことによって消費されthen(function)
、機能が実行されたプロデューサーのコンテキストですぐにプロデューサーの呼び出しの後resolve
。A Future
さんの結果は、呼び出しによって消費されget
、その後、生産者スレッドが値を生成するまで待つように、消費者のスレッドを起こし、消費者にそれを処理します。 Future
本質的にマルチスレッド化されていますが...
Promise
単一のスレッドのみでa を使用することは完全に可能です(実際、それは最初にそれらが最初に設計された正確な環境です。JavaScriptアプリケーションは一般に単一のスレッドしか持たないため、Future
そこに実装することはできません)。 Promise
したがって、はより軽量で効率的ですがFuture
、Future
より複雑で、Promise
s を使用して簡単に配置できないスレッド間の連携が必要な状況で役立ちます。要約するPromise
と、はプッシュモデル、Future
はプルモデルです(Iterable vs Observableを参照)
XMLHttpRequest
)として実行されます)。私は効率性の主張を信じていません、あなたはたまたまいくつかの数字を持っていますか?+++とはいえ、とても良い説明です。
get
、未解決の呼び出しにFuture
は必ず2つのスレッドコンテキストスイッチが必要であり、少なくとも数年前には約50 us必要でした。
クライアントコードの場合、Promiseは結果が利用可能になったときにコールバックを監視またはアタッチするためのものですが、Futureは結果を待ってから続行するためのものです。理論的には、promiseでできることを先物で行うことができるものは何でも、スタイルの違いにより、異なる言語でのpromiseの結果のAPIはチェーンを簡単にします。
Promise
、それを維持することはあなた次第です。他の誰かがあなたに約束をするとき、あなたは彼らがそれを尊重するかどうかを待つ必要がありますFuture