Scala Language
先物
サーチ…
未来を創る
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object FutureDivider {
def divide(a: Int, b: Int): Future[Int] = Future {
// Note that this is integer division.
a / b
}
}
簡単に言えば、 divide
方法は、商と解決将来作成オーバーa
b
。
将来を成功させる
成功した未来を消費する最も簡単な方法は、未来の価値を得ることです。 map
メソッドを使用することです。いくつかのコードがFutureDivider
オブジェクトのdivide
メソッドを "Creating a Future"の例からFutureDivider
ます。コードはの商取得するように見えるためには何が必要でしょうオーバーa
b
?
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(quotient)
}
}
}
失敗した未来を消費する
将来の計算で例外が作成されることがあり、これによりFutureが失敗することがあります。 "Creating a future"の例では、呼び出しコードが55
と0
をdivide
メソッドに渡すとどうなりますか?もちろん、0で除算しようとするとArithmeticException
がスローされます。それは消費するコードでどのように処理されますか?実際には、失敗に対処する方法がいくつかあります。
recover
とパターンマッチングで例外を処理します。
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
例外がfailed
投影を処理します。ここで例外は未来の値になります。
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
// Note the use of the dot operator to get the failed projection and map it.
eventualQuotient.failed.map {
ex => println(s"It failed with: ${ex.getMessage}")
}
}
}
未来を一緒にする
前の例では、成功と失敗のケースを処理するFutureの個々の機能を示しました。しかし、通常、両方の機能ははるかに簡潔に処理されます。ここに例があります、もっと現実的なやり方で書かれています:
object Calculator {
def calculateAndReport(a: Int, b: Int) = {
val eventualQuotient = FutureDivider divide(a, b)
eventualQuotient map {
quotient => println(s"Quotient: $quotient")
} recover {
case ex: ArithmeticException => println(s"It failed with: ${ex.getMessage}")
}
}
}
シーケンシングとトラバース先物
場合によっては、別々の先物で変動額を計算する必要があります。 List[Future[Int]]
を持つと仮定しますが、代わりにList[Int]
を処理する必要があります。それから問題は、 List[Future[Int]]
このインスタンスをFuture[List[Int]]
です。この目的のために、 Future
コンパニオンオブジェクトにはsequence
メソッドがあります。
def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.sequence(listOfFuture)
一般的なsequence
は、 F[G[T]]
をF
とG
制限してG[F[T]]
変換する関数型プログラミングの世界で一般的に知られている演算子です。
traverse
と呼ばれる代替演算子がありますが、同様の動作をしますが、余分な引数として関数を取ります。 x => x
という識別関数をパラメータとして使用すると、 sequence
演算子のように動作します。
def listOfFuture: List[Future[Int]] = List(1,2,3).map(Future(_))
def futureOfList: Future[List[Int]] = Future.traverse(listOfFuture)(x => x)
しかし、余分な引数は、指定されたlistOfFuture
内の将来の各インスタンスを変更することを可能にします。さらに、最初の議論はFuture
リストである必要はありません。したがって、この例を次のように変換することができます。
def futureOfList: Future[List[Int]] = Future.traverse(List(1,2,3))(Future(_))
この場合、 List(1,2,3)
は最初の引数として直接渡され、識別関数x => x
は関数Future(_)
に置き換えられ、同様に各Int
値をFuture
にラップします。これの利点は、パフォーマンスを向上させるために中間List[Future[Int]]
を省略できることです。
複数の先物を組み合わせる - 理解のために
forの理解は、複数の先物の成功した結果に依存するコードのブロックを実行するコンパクトな方法です。
f1, f2, f3
3つのFuture[String]
それぞれone, two, three
の文字列が含まれます。
val fCombined =
for {
s1 <- f1
s2 <- f2
s3 <- f3
} yield (s"$s1 - $s2 - $s3")
fCombined
は、すべての先物が正常に完了すると、文字列one - two - three
を含むFuture[String]
なります。
暗黙のExectionContextがここで仮定されることに注意してください。
また、理解のためには、flatMapメソッドの単なる構文上の砂糖なので、body内部のFutureオブジェクト構造は、先物で囲まれたコードブロックの並行実行を排除し、シーケンシャルコードにつながることに注意してください。あなたはそれを例に見ています:
val result1 = for {
first <- Future {
Thread.sleep(2000)
System.currentTimeMillis()
}
second <- Future {
Thread.sleep(1000)
System.currentTimeMillis()
}
} yield first - second
val fut1 = Future {
Thread.sleep(2000)
System.currentTimeMillis()
}
val fut2 = Future {
Thread.sleep(1000)
System.currentTimeMillis()
}
val result2 = for {
first <- fut1
second <- fut2
} yield first - second
result1
オブジェクトで囲まれた値は常に負であり、 result2
は正であります。
一般的な理解とyield
詳細については、 http://docs.scala-lang.org/tutorials/FAQ/yield.htmlを参照してください。