Future
との違いは何Promise
ですか?
どちらも将来の結果のプレースホルダーのように機能しますが、主な違いはどこにありますか?
9 に答える
(これまでの回答に完全に満足しているわけではないので、ここに私の試みがあります...)
ケビン・ライトのコメントだと思います
あなたは約束をすることができ、それを守るのはあなた次第です。他の誰かがあなたに約束したとき、あなたは彼らが将来それを尊重するかどうかを確認するのを待たなければなりません
かなりうまくまとめられていますが、いくつかの説明が役立つ場合があります。
Future と Promiseは非常によく似た概念です。違いは、Future はまだ存在しない結果の読み取り専用コンテナーであるのに対し、Promise は (通常は 1 回だけ) 書き込むことができるということです。Java 8 CompletableFutureと Guava SettableFutureは、値を設定 (「完了」) できるため、promise と考えることができますが、Future インターフェースも実装しているため、クライアントに違いはありません。
将来の結果は、非同期計算の結果によって、「他の誰か」によって設定されます。FutureTask (古典的な未来)は Callable または Runnable で初期化する必要があり、引数のないコンストラクターはなく、Future と FutureTask の両方が外部からの読み取り専用であることに注意してください (FutureTask の set メソッドは保護されています)。値は内部からの計算結果に設定されます。
一方、Promise の結果は、パブリック セッター メソッドを備えているため、「あなた」 (または実際には誰でも) によっていつでも設定できます。CompletableFuture と SettableFuture はどちらもタスクなしで作成でき、それらの値はいつでも設定できます。Promise をクライアント コードに送信し、後で必要に応じて実行します。
CompletableFuture は「純粋な」プロミスではなく、FutureTask と同じようにタスクで初期化できることに注意してください。その最も便利な機能は、処理ステップの無関係な連鎖です。
また、promise は future のサブタイプである必要はなく、同じオブジェクトである必要もないことに注意してください。Scala では、Future オブジェクトは非同期計算または別のPromise オブジェクトによって作成されます。C++ の場合も同様です。promise オブジェクトはプロデューサーによって使用され、future オブジェクトはコンシューマーによって使用されます。この分離の利点は、クライアントが将来の値を設定できないことです。
SpringとEJB 3.1の両方に、Scala/C++ の約束に似た AsyncResult クラスがあります。AsyncResult は Future を実装しますが、これは本当の未来ではありません。Spring/EJB の非同期メソッドは、バックグラウンド マジックを介して別の読み取り専用の Future オブジェクトを返します。この 2 番目の「本当の」未来は、クライアントが結果にアクセスするために使用できます。
この議論によると、Promise
最終的CompletableFuture
に Java 8 に含めるように求められており、その javadocは次のように説明しています。
明示的に完了する (値とステータスを設定する) ことができ、完了時にトリガーされる依存関数とアクションをサポートする CompletionStage として使用できる Future。
リストには例も示されています。
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
最終的な API は少し異なりますが、同様の非同期実行が可能であることに注意してください。
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
すでに受け入れられている回答があることは承知していますが、それでも2セント追加したいと思います。
TLDR: Future と Promise は非同期操作の 2 つの側面です: consumer/caller vs. producer/implementorです。
非同期 API メソッドの呼び出し元Future
として、計算結果へのハンドルとして を取得します。get()
たとえば、計算が完了するのを待って結果を取得するために呼び出すことができます。
この API メソッドが実際にどのように実装されているかを考えてみましょう。実装Future
者はすぐにを返さなければなりません。彼らは、計算が完了するとすぐにその未来を完了する責任があります (ディスパッチロジックを実装しているため、彼らはそれを知っています;-))。Promise
まさにそれを行うために/を使用CompletableFuture
します: を構築してすぐに返し、計算が完了したらCompletableFuture
呼び出します。complete(T result)
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) を使用し、父親が家にいなくて、母親だけが「遅い財布」を持っている場合、母親がお金を得るのに必要な期間よりも長くプログラムが生きている場合にのみ、彼女の約束は完了します。財布。
デフォルトのエグゼキュータは「デーモン」のような働きをし、すべての約束が果たされるのを待ちません。この事実についての適切な説明は見つかりませんでした...
これが答えになるかどうかはわかりませんが、他の人が誰かのために言ったことを見ると、これらの概念の両方に2つの別々の抽象化が必要なように見えるかもしれませんFuture
。Promise
) ...しかし、実際にはこれは必要ありません。
たとえば、プロミスが JavaScript でどのように定義されているかを見てみましょう。
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) {....から) を削除するだけです)。
実際、それらの抽象化は promise コンストラクターとして非常に優れています。
new Promise( function(resolve, reject) { /* do it */ } );
2 つのコールバックを提供できます。これらを使用して、Promise
正常に完了したり、エラーが発生したりできます。を構築するコードのみがPromise
それを完了することができ、既に構築されたオブジェクトを受け取るコードにPromise
は読み取り専用ビューがあります。
resolveとrejectが保護されたメソッドである場合、継承を使用して上記を実現できます。
この例では、呼び出しの非同期シーケンスを作成するために Java で Promises を使用する方法を確認できます。
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));