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


275

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


99
あなたが作ることができPromise、それを維持することはあなた次第です。他の誰かがあなたに約束をするとき、あなたは彼らがそれを尊重するかどうかを待つ必要がありますFuture
ケビン・ライト

1
ウィキペディア先物と約束
wener

30
私が今まで読んだ中で
最も役に立た

回答:


146

この議論によると、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);

78
それはあなたのせいのAssyliasではありませんが、そのjavadoc抽出はまともな技術作者による深刻な改修を必要とします。私の5回目のリードスルーで、私はそれが何を言おうとしているのかを理解し始めることができます...そして、私はすでに存在している未来と約束を理解してこれにたどり着きます!
ビートルートビートルート2013年

2
@ Beetroot-Beetrootそれは今までに起こったようです。
ヘルマン

1
@hermanありがとう-javadocの最終バージョンを指すようにリンクを更新しました。
assylias

7
@ Beetroot-Beetroot Exceptionallyメソッドについては、ドキュメントをチェックしてください。それは素晴らしい詩になるでしょうが、読みやすいドキュメントの例外的な失敗です。
Fulluphigh 2015年

4
疑問に思う方のために、@ Fulluphighはこれを参照しています。Java 8で削除/オーバーホールされたようです
Cedric Reichenbach 2017年

148

(私はこれまでのところ完全に満足しているわけではないので、これが私の試みです...)

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番目の「実際の」フューチャーは、クライアントが結果にアクセスするために使用できます。


116

私はすでに受け入れられた回答があることを認識していますが、それでも私の2セントを追加したいと思います。

TLDR:フューチャーとプロミスは非同期操作の2つの側面です:コンシューマー/呼び出し側プロデューサー/実装者

非同期APIメソッドの呼び出し元としてFuture、計算結果のハンドルとしてを取得します。たとえばget()、計算が完了するのを待って結果を取得するためにそれを呼び出すことができます。

次に、このAPIメソッドが実際にどのように実装されるかを考えてみましょう。実装者Futureただちに返す必要があります。彼らは、計算が完了するとすぐにその未来を完了する責任があります(ディスパッチロジックを実装しているので、それはわかっています;-))。彼らはPromise/ CompletableFutureを使ってそれだけを行います:を構築してCompletableFutureすぐに返しcomplete(T result)、計算が完了したら呼び出します。


1
これは、Promiseが常にFutureのサブクラスであり、Futureの書き込み可能性がタイプによって隠されていることを意味しますか?
devios1 2018

それが暗示されているとは思いません。実装面では、多くの場合それが当てはまります(例:Java、Scala)。
RahelLüthy18年

74

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)を使用し、父親が家にいなくても、「スローパース」を持っている母親だけがいる場合、プログラムの寿命がママからのお金よりも長くなければ、彼女の約束は完了しません。財布。

デフォルトのエグゼキューターは「デーモン」のような働きをし、すべての約束が満たされるのを待ちません。私はこの事実の良い説明を見つけていません...


8
これを読むのはとても楽しいです!将来を忘れて約束することはもうできないと思います。
user1532146 2017

2
これは答えとして受け入れられなければなりません。物語を読むのと同じです。ありがとう@Vladimir
Phillen

ありがとう@Vladimir
intvprep

9

これが答えになるかどうかはわかりませんが、他の人が誰かに対して言ったことを見ると、これらの概念の両方に2つの別個の抽象化が必要であるように見える可能性があるため、一方(Future)は他方の読み取り専用ビュー(Promise)...しかし、実際にはこれは必要ありません。

たとえば、javascriptでpromiseがどのように定義されているかを見てみましょう。

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オブジェクトを受け取るコードは読み取り専用ビューを持ちます。

継承を使用すると、解決拒否が保護されたメソッドであれば、上記を実現できます。


4
+1。これがこの質問に対する正しい答えです。 CompletableFutureいくつかの類似性を持っているかもしれませんPromiseが、それはまだありませんPromise、消費されることを意図しています方法が異なるため:Promiseの結果を呼び出すことによって消費されthen(function)、機能が実行されたプロデューサーのコンテキストですぐにプロデューサーの呼び出しの後resolve。A Futureさんの結果は、呼び出しによって消費されget、その後、生産者スレッドが値を生成するまで待つように、消費者のスレッドを起こし、消費者にそれを処理します。 Future本質的にマルチスレッド化されていますが...
Periata Breatta

5
... Promise単一のスレッドのみでa を使用することは完全に可能です(実際、それは最初にそれらが最初に設計された正確な環境です。JavaScriptアプリケーションは一般に単一のスレッドしか持たないため、Futureそこに実装することはできません)。 Promiseしたがって、はより軽量で効率的ですがFutureFutureより複雑で、Promises を使用して簡単に配置できないスレッド間の連携が必要な状況で役立ちます。要約するPromiseと、はプッシュモデル、Futureはプルモデルです(Iterable vs Observableを参照)
Periata Breatta

@PeriataBreattaシングルスレッド環境でも、約束を果たす何かが必要です(通常は、別のスレッド(たとえば、XMLHttpRequest)として実行されます)。私は効率性の主張を信じていません、あなたはたまたまいくつかの数字を持っていますか?+++とはいえ、とても良い説明です。
maaartinus

1
@maaartinus-はい、何かは約束を満たさなければなりませんが、外部状態の変化をポーリングし、終了したアクションに関連する約束を解決するトップレベルのループを使用することで実現できます(実際、多くの場合そうです)。効率的には、特にPromiseの具体的な数値はありませんがget、未解決の呼び出しにFutureは必ず2つのスレッドコンテキストスイッチが必要であり、少なくとも数年前には約50 us必要でした。
Periata Breatta

@PeriataBreatta実際にあなたのコメントは受け入れられる解決策であるべきです。私はあなたのような説明(プル/プッシュ、シングル/マルチスレッド)を探していました。
トーマスジェイコブ

5

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


2

Futureインターフェースにはsetメソッドはなく、getメソッドのみなので、読み取り専用です。CompletableFutureについては、この記事が参考になるかもしれません。 完成可能な

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.