
皆さん、こんばんは。 今日は、
Futureと呼ばれるこのようなすばらしい
scalaコアの部分に光を当てたいと思います。 実際には公式Webサイトに
ドキュメントがありますが、
イベント駆動型アプローチを使用してそれを操作する方法の説明があります。 しかし、
Futureもモナドです。 そして、この記事では、例を挙げて、この方法でそれらを使用する方法を少し説明したかったのです(むしろ、この問題に対する私のビジョン)。 みんなに猫の下の質問を読んでもらいます。
ここにないもの
この記事では、コールバック、約束、この優れた競争モデルの達成方法などについては説明しません。
どうなる
そして、
Futureモナドに動作を与える関数についてだけ書いていきます。
地図
それでは、
apiを見てみましょう。
def map[S](f: (T) ⇒ S)(implicit executor: ExecutionContext): Future[S]
mapは何をし
ますか?
Futureが成功した場合、
f関数が実行され、結果が渡されます。 そして、この結果は
今後もパッケージ化されます。
Future(5) map (2*) map println
最終的に、このコードは画面に10を期待どおりに表示します。 結果が
Failureの場合、関数は実行されません。
Future(5) map (_ / 0) map println
2番目の関数は例外をスローし、何も表示されません。
では、なぜ
mapを使用
するのですか? -前の結果を必要とする順次操作の場合。
flatMap
経験豊富な読者は、モナドがモナドの内部にある可能性があることを理解しており、この問題を何らかの方法で修正する必要があります。
def flatMap[S](f: (T) ⇒ Future[S])(implicit executor: ExecutionContext): Future[S]
flatMapは、別の
Futureを返す関数を受け取り、それを返します。
def f(a: Int): Future[Int] = Future(a * 5) Future(2) flatMap (f) map println
数字の10が表示され、実行に失敗した場合の動作は
mapの場合と同じです。
Futureを返すチェーン操作の場合は、
flatMapを使用する必要があります。
のために
飛ぶ必要はありませんし、怒ってそれ
を書く必要はありません。関数ではなく、構文構造です。 はい、知っていますが、彼女について話さないのは愚かなことです
mapと
flatMapについて説明したので、
for-comprehensionsの使用を検討する必要があります。
Futureで forを使用
する悪い例と良い例を見てみましょう。
両方の操作の結果は同じになりますが、...
どうして悪いの?実は、答えは簡単です。
for { a <- longComputations1() b <- longComputations2() c <- longComputations3() } yield a*b*c
展開先:
longComputations1().flatMap { a => longComputations2().flatMap { b => longComputations3().flatMap { c => a*b*c } } }
つまり、全体ではなく、順番に呼び出されます。 2番目のオプションはこの問題を解決しますが、最善の方法ではないため、より良い方法です。
zip
def zip[U](that: Future[U]): Future[(T, U)]
以前は、複数の
Futureを同時に使用する問題が考慮されていました。 まあ、
zipはこの問題を解決します。 彼は2つの
Futureを
取得し 、その結果を
Tuple2にパックします。 そして、これが今書かれている上記の例です:
longComputations1() zip longComputations2() zip longComputations3() map { case ((a, b), c) => a * b * c }
個人的には、私の意見では、すべてがずっときれいでシンプルです。
フィルターとwithFilter
def filter(p: (T) ⇒ Boolean)(implicit executor: ExecutionContext): Future[T] final def withFilter(p: (T) ⇒ Boolean)(implicit executor: ExecutionContext): Future[T]
ここではすべてが論理的であり、結果を取得してテストし、適合しない場合は、
失敗した Futureがあり、その中に
NoSuchElementExceptionがパックされます。
recoverおよびrecoverWith
コードは非同期で実行されるため、例外の非同期制御が必要です。 そして、ここにあります:
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U]
例外の場合、部分関数が呼び出され、ある場合には値を返し、別の
Futureでは値を返します。
Future(5) map (_ / 0) recover { case _ => 0 } map println
ここでは、例外が処理され、0が表示されます。
foreach
実際、それは
マップであり、結果のみが新しい
Futureにパッケージ化されず、単にUnitを返します:
def foreach[U](f: (T) ⇒ U)(implicit executor: ExecutionContext): Unit
実際、前の例は完全に正しいわけではなく、次のように書く方が良いでしょう。
Future(5) map (2*) foreach println
ここでは、1つの余分なFutureの作成を避けました。
より一般的な例
だから私たちは持っています:
def f1: Future[Double] def f2: Future[Double] def f3(a: Double): Future[Double] def f4: Future[Double] def f5(a: Double, b: Double, c: Double): Future[Double]
f2の実行結果は
f3に渡す必要があり、
f1 、
f3 、および
f4の実行結果は
f5に渡す必要があることがわかっています。 そして最終的に、結果は標準出力に出力されるはずです。 また、f3が例外をスローすることもあり、その場合は
0が返されます。
行こう:
val r1 = f1() val r2 = f2() flatMap (f3) recover { case _: Exception => 0 } var r3 = f4() for { a <- r1 b <- r2 c <- r3 res <- f5(a, b, c) } yield println(res)
私が好む:
f1 zip f4 zip (f2() flatMap (f3) recover { case _: Exception => 0 }) flatMap { case ((a, b), c) => f5(a, b, c) } foreach println
そして、どのように記録しますか?
あとがき
だから私は
Futureの単項関数を説明しようとしましたが、個人的には成功したと思います。 もちろん、たとえば、ヘルパークラスについてはまだ説明することがあります。ヘルパークラスには非常に興味深い重要な機能がありますが、これはすでに別の記事の資料です。
Futureをどのように使用していて、私が省略したコメントにコメントを書いてください。
ご清聴ありがとうございました。