Scalaの未来に戻る



皆さん、こんばんは。 今日は、 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を使用する必要があります。



のために


飛ぶ必要はありませんし、怒ってそれ書く必要はありません。関数ではなく、構文構造です。 はい、知っていますが、彼女について話さないのは愚かなことです

mapflatMapについて説明したので、 for-comprehensionsの使用を検討する必要があります。

Futureで forを使用する悪い例と良い例を見てみましょう。

 // for { a <- longComputations1() b <- longComputations2() c <- longComputations3() } yield a*b*c // val f1 = longComputations1() val f2 = longComputations2() val f3 = longComputations3() for { a <- f1 b <- f2 c <- f3 } yield a*b*c
      
      





両方の操作の結果は同じになりますが、...

どうして悪いの?
実は、答えは簡単です。

 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に渡す必要があり、 f1f3 、および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をどのように使用していて、私が省略したコメントにコメントを書いてください。

ご清聴ありがとうございました。



All Articles